root/lib/pacemaker/pcmk_sched_group.c

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

DEFINITIONS

This source file includes following definitions.
  1. pcmk__group_allocate
  2. group_create_actions
  3. group_update_pseudo_status
  4. group_internal_constraints
  5. group_rsc_colocation_lh
  6. group_rsc_colocation_rh
  7. group_action_flags
  8. group_update_actions
  9. group_rsc_location
  10. group_expand
  11. pcmk__group_merge_weights
  12. group_append_meta

   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 <stdbool.h>
  13 
  14 #include <crm/msg_xml.h>
  15 
  16 #include <pacemaker-internal.h>
  17 
  18 #define VARIANT_GROUP 1
  19 #include <lib/pengine/variant.h>
  20 
  21 pe_node_t *
  22 pcmk__group_allocate(pe_resource_t *rsc, pe_node_t *prefer,
     /* [previous][next][first][last][top][bottom][index][help] */
  23                      pe_working_set_t *data_set)
  24 {
  25     pe_node_t *node = NULL;
  26     pe_node_t *group_node = NULL;
  27     GList *gIter = NULL;
  28     group_variant_data_t *group_data = NULL;
  29 
  30     get_group_variant_data(group_data, rsc);
  31 
  32     if (!pcmk_is_set(rsc->flags, pe_rsc_provisional)) {
  33         return rsc->allocated_to;
  34     }
  35     if (pcmk_is_set(rsc->flags, pe_rsc_allocating)) {
  36         pe_rsc_debug(rsc, "Dependency loop detected involving %s", rsc->id);
  37         return NULL;
  38     }
  39 
  40     if (group_data->first_child == NULL) {
  41         // Nothing to allocate
  42         pe__clear_resource_flags(rsc, pe_rsc_provisional);
  43         return NULL;
  44     }
  45 
  46     pe__set_resource_flags(rsc, pe_rsc_allocating);
  47     rsc->role = group_data->first_child->role;
  48 
  49     group_data->first_child->rsc_cons =
  50         g_list_concat(group_data->first_child->rsc_cons, rsc->rsc_cons);
  51     rsc->rsc_cons = NULL;
  52 
  53     group_data->last_child->rsc_cons_lhs =
  54         g_list_concat(group_data->last_child->rsc_cons_lhs, rsc->rsc_cons_lhs);
  55     rsc->rsc_cons_lhs = NULL;
  56 
  57     pe__show_node_weights(!pcmk_is_set(data_set->flags, pe_flag_show_scores),
  58                           rsc, __func__, rsc->allowed_nodes, data_set);
  59 
  60     gIter = rsc->children;
  61     for (; gIter != NULL; gIter = gIter->next) {
  62         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
  63 
  64         pe_rsc_trace(rsc, "Allocating group %s member %s",
  65                      rsc->id, child_rsc->id);
  66         node = child_rsc->cmds->allocate(child_rsc, prefer, data_set);
  67         if (group_node == NULL) {
  68             group_node = node;
  69         }
  70     }
  71 
  72     pe__set_next_role(rsc, group_data->first_child->next_role,
  73                       "first group member");
  74     pe__clear_resource_flags(rsc, pe_rsc_allocating|pe_rsc_provisional);
  75 
  76     if (group_data->colocated) {
  77         return group_node;
  78     }
  79     return NULL;
  80 }
  81 
  82 void group_update_pseudo_status(pe_resource_t * parent, pe_resource_t * child);
  83 
  84 void
  85 group_create_actions(pe_resource_t * rsc, pe_working_set_t * data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
  86 {
  87     pe_action_t *op = NULL;
  88     const char *value = NULL;
  89     GList *gIter = rsc->children;
  90 
  91     pe_rsc_trace(rsc, "Creating actions for %s", rsc->id);
  92 
  93     for (; gIter != NULL; gIter = gIter->next) {
  94         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
  95 
  96         child_rsc->cmds->create_actions(child_rsc, data_set);
  97         group_update_pseudo_status(rsc, child_rsc);
  98     }
  99 
 100     op = start_action(rsc, NULL, TRUE /* !group_data->child_starting */ );
 101     pe__set_action_flags(op, pe_action_pseudo|pe_action_runnable);
 102 
 103     op = custom_action(rsc, started_key(rsc),
 104                        RSC_STARTED, NULL, TRUE /* !group_data->child_starting */ , TRUE, data_set);
 105     pe__set_action_flags(op, pe_action_pseudo|pe_action_runnable);
 106 
 107     op = stop_action(rsc, NULL, TRUE /* !group_data->child_stopping */ );
 108     pe__set_action_flags(op, pe_action_pseudo|pe_action_runnable);
 109 
 110     op = custom_action(rsc, stopped_key(rsc),
 111                        RSC_STOPPED, NULL, TRUE /* !group_data->child_stopping */ , TRUE, data_set);
 112     pe__set_action_flags(op, pe_action_pseudo|pe_action_runnable);
 113 
 114     value = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_PROMOTABLE);
 115     if (crm_is_true(value)) {
 116         op = custom_action(rsc, demote_key(rsc), RSC_DEMOTE, NULL, TRUE, TRUE, data_set);
 117         pe__set_action_flags(op, pe_action_pseudo|pe_action_runnable);
 118 
 119         op = custom_action(rsc, demoted_key(rsc), RSC_DEMOTED, NULL, TRUE, TRUE, data_set);
 120         pe__set_action_flags(op, pe_action_pseudo|pe_action_runnable);
 121 
 122         op = custom_action(rsc, promote_key(rsc), RSC_PROMOTE, NULL, TRUE, TRUE, data_set);
 123         pe__set_action_flags(op, pe_action_pseudo|pe_action_runnable);
 124 
 125         op = custom_action(rsc, promoted_key(rsc), RSC_PROMOTED, NULL, TRUE, TRUE, data_set);
 126         pe__set_action_flags(op, pe_action_pseudo|pe_action_runnable);
 127     }
 128 }
 129 
 130 void
 131 group_update_pseudo_status(pe_resource_t * parent, pe_resource_t * child)
     /* [previous][next][first][last][top][bottom][index][help] */
 132 {
 133     GList *gIter = child->actions;
 134     group_variant_data_t *group_data = NULL;
 135 
 136     get_group_variant_data(group_data, parent);
 137 
 138     if (group_data->ordered == FALSE) {
 139         /* If this group is not ordered, then leave the meta-actions as optional */
 140         return;
 141     }
 142 
 143     if (group_data->child_stopping && group_data->child_starting) {
 144         return;
 145     }
 146 
 147     for (; gIter != NULL; gIter = gIter->next) {
 148         pe_action_t *action = (pe_action_t *) gIter->data;
 149 
 150         if (pcmk_is_set(action->flags, pe_action_optional)) {
 151             continue;
 152         }
 153         if (pcmk__str_eq(RSC_STOP, action->task, pcmk__str_casei)
 154             && pcmk_is_set(action->flags, pe_action_runnable)) {
 155 
 156             group_data->child_stopping = TRUE;
 157             pe_rsc_trace(action->rsc, "Based on %s the group is stopping", action->uuid);
 158 
 159         } else if (pcmk__str_eq(RSC_START, action->task, pcmk__str_casei)
 160                    && pcmk_is_set(action->flags, pe_action_runnable)) {
 161             group_data->child_starting = TRUE;
 162             pe_rsc_trace(action->rsc, "Based on %s the group is starting", action->uuid);
 163         }
 164     }
 165 }
 166 
 167 void
 168 group_internal_constraints(pe_resource_t * rsc, pe_working_set_t * data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 169 {
 170     GList *gIter = rsc->children;
 171     pe_resource_t *last_rsc = NULL;
 172     pe_resource_t *last_active = NULL;
 173     pe_resource_t *top = uber_parent(rsc);
 174     group_variant_data_t *group_data = NULL;
 175 
 176     get_group_variant_data(group_data, rsc);
 177 
 178     new_rsc_order(rsc, RSC_STOPPED, rsc, RSC_START, pe_order_optional, data_set);
 179     new_rsc_order(rsc, RSC_START, rsc, RSC_STARTED, pe_order_runnable_left, data_set);
 180     new_rsc_order(rsc, RSC_STOP, rsc, RSC_STOPPED, pe_order_runnable_left, data_set);
 181 
 182     for (; gIter != NULL; gIter = gIter->next) {
 183         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 184         int stop = pe_order_none;
 185         int stopped = pe_order_implies_then_printed;
 186         int start = pe_order_implies_then | pe_order_runnable_left;
 187         int started =
 188             pe_order_runnable_left | pe_order_implies_then | pe_order_implies_then_printed;
 189 
 190         child_rsc->cmds->internal_constraints(child_rsc, data_set);
 191 
 192         if (last_rsc == NULL) {
 193             if (group_data->ordered) {
 194                 pe__set_order_flags(stop, pe_order_optional);
 195                 stopped = pe_order_implies_then;
 196             }
 197 
 198         } else if (group_data->colocated) {
 199             pcmk__new_colocation("group:internal_colocation", NULL, INFINITY,
 200                                  child_rsc, last_rsc, NULL, NULL,
 201                                  pcmk_is_set(child_rsc->flags, pe_rsc_critical),
 202                                  data_set);
 203         }
 204 
 205         if (pcmk_is_set(top->flags, pe_rsc_promotable)) {
 206             new_rsc_order(rsc, RSC_DEMOTE, child_rsc, RSC_DEMOTE,
 207                           stop | pe_order_implies_first_printed, data_set);
 208 
 209             new_rsc_order(child_rsc, RSC_DEMOTE, rsc, RSC_DEMOTED, stopped, data_set);
 210 
 211             new_rsc_order(child_rsc, RSC_PROMOTE, rsc, RSC_PROMOTED, started, data_set);
 212 
 213             new_rsc_order(rsc, RSC_PROMOTE, child_rsc, RSC_PROMOTE,
 214                           pe_order_implies_first_printed, data_set);
 215 
 216         }
 217 
 218         order_start_start(rsc, child_rsc, pe_order_implies_first_printed);
 219         order_stop_stop(rsc, child_rsc, stop | pe_order_implies_first_printed);
 220 
 221         new_rsc_order(child_rsc, RSC_STOP, rsc, RSC_STOPPED, stopped, data_set);
 222 
 223         new_rsc_order(child_rsc, RSC_START, rsc, RSC_STARTED, started, data_set);
 224 
 225         if (group_data->ordered == FALSE) {
 226             order_start_start(rsc, child_rsc, start | pe_order_implies_first_printed);
 227             if (pcmk_is_set(top->flags, pe_rsc_promotable)) {
 228                 new_rsc_order(rsc, RSC_PROMOTE, child_rsc, RSC_PROMOTE,
 229                               start | pe_order_implies_first_printed, data_set);
 230             }
 231 
 232         } else if (last_rsc != NULL) {
 233             child_rsc->restart_type = pe_restart_restart;
 234 
 235             order_start_start(last_rsc, child_rsc, start);
 236             order_stop_stop(child_rsc, last_rsc, pe_order_optional | pe_order_restart);
 237 
 238             if (pcmk_is_set(top->flags, pe_rsc_promotable)) {
 239                 new_rsc_order(last_rsc, RSC_PROMOTE, child_rsc, RSC_PROMOTE, start, data_set);
 240                 new_rsc_order(child_rsc, RSC_DEMOTE, last_rsc, RSC_DEMOTE, pe_order_optional,
 241                               data_set);
 242             }
 243 
 244         } else {
 245             /* If anyone in the group is starting, then
 246              *  pe_order_implies_then will cause _everyone_ in the group
 247              *  to be sent a start action
 248              * But this is safe since starting something that is already
 249              *  started is required to be "safe"
 250              */
 251             int flags = pe_order_none;
 252 
 253             order_start_start(rsc, child_rsc, flags);
 254             if (pcmk_is_set(top->flags, pe_rsc_promotable)) {
 255                 new_rsc_order(rsc, RSC_PROMOTE, child_rsc, RSC_PROMOTE, flags, data_set);
 256             }
 257 
 258         }
 259 
 260         /* Look for partially active groups
 261          * Make sure they still shut down in sequence
 262          */
 263         if (child_rsc->running_on) {
 264             if (group_data->ordered
 265                 && last_rsc
 266                 && last_rsc->running_on == NULL && last_active && last_active->running_on) {
 267                 order_stop_stop(child_rsc, last_active, pe_order_optional);
 268             }
 269             last_active = child_rsc;
 270         }
 271 
 272         last_rsc = child_rsc;
 273     }
 274 
 275     if (group_data->ordered && last_rsc != NULL) {
 276         int stop_stop_flags = pe_order_implies_then;
 277         int stop_stopped_flags = pe_order_optional;
 278 
 279         order_stop_stop(rsc, last_rsc, stop_stop_flags);
 280         new_rsc_order(last_rsc, RSC_STOP, rsc, RSC_STOPPED, stop_stopped_flags, data_set);
 281 
 282         if (pcmk_is_set(top->flags, pe_rsc_promotable)) {
 283             new_rsc_order(rsc, RSC_DEMOTE, last_rsc, RSC_DEMOTE, stop_stop_flags, data_set);
 284             new_rsc_order(last_rsc, RSC_DEMOTE, rsc, RSC_DEMOTED, stop_stopped_flags, data_set);
 285         }
 286     }
 287 }
 288 
 289 void
 290 group_rsc_colocation_lh(pe_resource_t *rsc_lh, pe_resource_t *rsc_rh,
     /* [previous][next][first][last][top][bottom][index][help] */
 291                         pcmk__colocation_t *constraint,
 292                         pe_working_set_t *data_set)
 293 {
 294     GList *gIter = NULL;
 295     group_variant_data_t *group_data = NULL;
 296 
 297     if (rsc_lh == NULL) {
 298         pe_err("rsc_lh was NULL for %s", constraint->id);
 299         return;
 300 
 301     } else if (rsc_rh == NULL) {
 302         pe_err("rsc_rh was NULL for %s", constraint->id);
 303         return;
 304     }
 305 
 306     gIter = rsc_lh->children;
 307     pe_rsc_trace(rsc_lh, "Processing constraints from %s", rsc_lh->id);
 308 
 309     get_group_variant_data(group_data, rsc_lh);
 310 
 311     if (group_data->colocated) {
 312         group_data->first_child->cmds->rsc_colocation_lh(group_data->first_child,
 313                                                          rsc_rh, constraint,
 314                                                          data_set);
 315         return;
 316 
 317     } else if (constraint->score >= INFINITY) {
 318         pcmk__config_err("%s: Cannot perform mandatory colocation "
 319                          "between non-colocated group and %s",
 320                          rsc_lh->id, rsc_rh->id);
 321         return;
 322     }
 323 
 324     for (; gIter != NULL; gIter = gIter->next) {
 325         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 326 
 327         child_rsc->cmds->rsc_colocation_lh(child_rsc, rsc_rh, constraint,
 328                                            data_set);
 329     }
 330 }
 331 
 332 void
 333 group_rsc_colocation_rh(pe_resource_t *rsc_lh, pe_resource_t *rsc_rh,
     /* [previous][next][first][last][top][bottom][index][help] */
 334                         pcmk__colocation_t *constraint,
 335                         pe_working_set_t *data_set)
 336 {
 337     GList *gIter = rsc_rh->children;
 338     group_variant_data_t *group_data = NULL;
 339 
 340     get_group_variant_data(group_data, rsc_rh);
 341     CRM_CHECK(rsc_lh->variant == pe_native, return);
 342 
 343     pe_rsc_trace(rsc_rh, "Processing RH %s of constraint %s (LH is %s)",
 344                  rsc_rh->id, constraint->id, rsc_lh->id);
 345 
 346     if (pcmk_is_set(rsc_rh->flags, pe_rsc_provisional)) {
 347         return;
 348 
 349     } else if (group_data->colocated && group_data->first_child) {
 350         if (constraint->score >= INFINITY) {
 351             /* Ensure RHS is _fully_ up before can start LHS */
 352             group_data->last_child->cmds->rsc_colocation_rh(rsc_lh,
 353                                                             group_data->last_child,
 354                                                             constraint,
 355                                                             data_set);
 356         } else {
 357             /* A partially active RHS is fine */
 358             group_data->first_child->cmds->rsc_colocation_rh(rsc_lh,
 359                                                              group_data->first_child,
 360                                                              constraint,
 361                                                              data_set);
 362         }
 363 
 364         return;
 365 
 366     } else if (constraint->score >= INFINITY) {
 367         pcmk__config_err("%s: Cannot perform mandatory colocation with"
 368                          " non-colocated group %s", rsc_lh->id, rsc_rh->id);
 369         return;
 370     }
 371 
 372     for (; gIter != NULL; gIter = gIter->next) {
 373         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 374 
 375         child_rsc->cmds->rsc_colocation_rh(rsc_lh, child_rsc, constraint,
 376                                            data_set);
 377     }
 378 }
 379 
 380 enum pe_action_flags
 381 group_action_flags(pe_action_t * action, pe_node_t * node)
     /* [previous][next][first][last][top][bottom][index][help] */
 382 {
 383     GList *gIter = NULL;
 384     enum pe_action_flags flags = (pe_action_optional | pe_action_runnable | pe_action_pseudo);
 385 
 386     for (gIter = action->rsc->children; gIter != NULL; gIter = gIter->next) {
 387         pe_resource_t *child = (pe_resource_t *) gIter->data;
 388         enum action_tasks task = get_complex_task(child, action->task, TRUE);
 389         const char *task_s = task2text(task);
 390         pe_action_t *child_action = find_first_action(child->actions, NULL, task_s, node);
 391 
 392         if (child_action) {
 393             enum pe_action_flags child_flags = child->cmds->action_flags(child_action, node);
 394 
 395             if (pcmk_is_set(flags, pe_action_optional)
 396                 && !pcmk_is_set(child_flags, pe_action_optional)) {
 397                 pe_rsc_trace(action->rsc, "%s is mandatory because of %s", action->uuid,
 398                              child_action->uuid);
 399                 pe__clear_raw_action_flags(flags, "group action",
 400                                            pe_action_optional);
 401                 pe__clear_action_flags(action, pe_action_optional);
 402             }
 403             if (!pcmk__str_eq(task_s, action->task, pcmk__str_casei)
 404                 && pcmk_is_set(flags, pe_action_runnable)
 405                 && !pcmk_is_set(child_flags, pe_action_runnable)) {
 406 
 407                 pe_rsc_trace(action->rsc, "%s is not runnable because of %s", action->uuid,
 408                              child_action->uuid);
 409                 pe__clear_raw_action_flags(flags, "group action",
 410                                            pe_action_runnable);
 411                 pe__clear_action_flags(action, pe_action_runnable);
 412             }
 413 
 414         } else if (task != stop_rsc && task != action_demote) {
 415             pe_rsc_trace(action->rsc, "%s is not runnable because of %s (not found in %s)",
 416                          action->uuid, task_s, child->id);
 417             pe__clear_raw_action_flags(flags, "group action",
 418                                        pe_action_runnable);
 419         }
 420     }
 421 
 422     return flags;
 423 }
 424 
 425 enum pe_graph_flags
 426 group_update_actions(pe_action_t *first, pe_action_t *then, pe_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
 427                      enum pe_action_flags flags, enum pe_action_flags filter,
 428                      enum pe_ordering type, pe_working_set_t *data_set)
 429 {
 430     GList *gIter = then->rsc->children;
 431     enum pe_graph_flags changed = pe_graph_none;
 432 
 433     CRM_ASSERT(then->rsc != NULL);
 434     changed |= native_update_actions(first, then, node, flags, filter, type,
 435                                      data_set);
 436 
 437     for (; gIter != NULL; gIter = gIter->next) {
 438         pe_resource_t *child = (pe_resource_t *) gIter->data;
 439         pe_action_t *child_action = find_first_action(child->actions, NULL, then->task, node);
 440 
 441         if (child_action) {
 442             changed |= child->cmds->update_actions(first, child_action, node,
 443                                                    flags, filter, type,
 444                                                    data_set);
 445         }
 446     }
 447 
 448     return changed;
 449 }
 450 
 451 void
 452 group_rsc_location(pe_resource_t *rsc, pe__location_t *constraint)
     /* [previous][next][first][last][top][bottom][index][help] */
 453 {
 454     GList *gIter = rsc->children;
 455     GList *saved = constraint->node_list_rh;
 456     GList *zero = pcmk__copy_node_list(constraint->node_list_rh, true);
 457     gboolean reset_scores = TRUE;
 458     group_variant_data_t *group_data = NULL;
 459 
 460     get_group_variant_data(group_data, rsc);
 461 
 462     pe_rsc_debug(rsc, "Processing rsc_location %s for %s", constraint->id, rsc->id);
 463 
 464     native_rsc_location(rsc, constraint);
 465 
 466     for (; gIter != NULL; gIter = gIter->next) {
 467         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 468 
 469         child_rsc->cmds->rsc_location(child_rsc, constraint);
 470         if (group_data->colocated && reset_scores) {
 471             reset_scores = FALSE;
 472             constraint->node_list_rh = zero;
 473         }
 474     }
 475 
 476     constraint->node_list_rh = saved;
 477     g_list_free_full(zero, free);
 478 }
 479 
 480 void
 481 group_expand(pe_resource_t * rsc, pe_working_set_t * data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 482 {
 483     CRM_CHECK(rsc != NULL, return);
 484 
 485     pe_rsc_trace(rsc, "Processing actions from %s", rsc->id);
 486     native_expand(rsc, data_set);
 487 
 488     for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
 489         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 490 
 491         child_rsc->cmds->expand(child_rsc, data_set);
 492     }
 493 }
 494 
 495 GHashTable *
 496 pcmk__group_merge_weights(pe_resource_t *rsc, const char *rhs,
     /* [previous][next][first][last][top][bottom][index][help] */
 497                           GHashTable *nodes, const char *attr, float factor,
 498                           uint32_t flags)
 499 {
 500     GList *gIter = rsc->rsc_cons_lhs;
 501     group_variant_data_t *group_data = NULL;
 502 
 503     get_group_variant_data(group_data, rsc);
 504 
 505     if (pcmk_is_set(rsc->flags, pe_rsc_merging)) {
 506         pe_rsc_info(rsc, "Breaking dependency loop with %s at %s", rsc->id, rhs);
 507         return nodes;
 508     }
 509 
 510     pe__set_resource_flags(rsc, pe_rsc_merging);
 511 
 512     nodes =
 513         group_data->first_child->cmds->merge_weights(group_data->first_child, rhs, nodes, attr,
 514                                                      factor, flags);
 515 
 516     for (; gIter != NULL; gIter = gIter->next) {
 517         pcmk__colocation_t *constraint = (pcmk__colocation_t *) gIter->data;
 518 
 519         nodes = pcmk__native_merge_weights(constraint->rsc_lh, rsc->id, nodes,
 520                                            constraint->node_attribute,
 521                                            constraint->score / (float) INFINITY,
 522                                            flags);
 523     }
 524 
 525     pe__clear_resource_flags(rsc, pe_rsc_merging);
 526     return nodes;
 527 }
 528 
 529 void
 530 group_append_meta(pe_resource_t * rsc, xmlNode * xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 531 {
 532 }

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