diff --git a/pengine/clone.c b/pengine/clone.c index a51677ab60..a79271e2e5 100644 --- a/pengine/clone.c +++ b/pengine/clone.c @@ -1,1649 +1,1651 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #define VARIANT_CLONE 1 #include gint sort_clone_instance(gconstpointer a, gconstpointer b, gpointer data_set); static void append_parent_colocation(resource_t * rsc, resource_t * child, gboolean all); static gint sort_rsc_id(gconstpointer a, gconstpointer b) { const resource_t *resource1 = (const resource_t *)a; const resource_t *resource2 = (const resource_t *)b; CRM_ASSERT(resource1 != NULL); CRM_ASSERT(resource2 != NULL); return strcmp(resource1->id, resource2->id); } static node_t * parent_node_instance(const resource_t * rsc, node_t * node) { node_t *ret = NULL; if (node != NULL && rsc->parent) { ret = pe_hash_table_lookup(rsc->parent->allowed_nodes, node->details->id); } else if(node != NULL) { ret = pe_hash_table_lookup(rsc->allowed_nodes, node->details->id); } return ret; } static gboolean did_fail(const resource_t * rsc) { GListPtr gIter = rsc->children; if (is_set(rsc->flags, pe_rsc_failed)) { return TRUE; } for (; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t *) gIter->data; if (did_fail(child_rsc)) { return TRUE; } } return FALSE; } gint sort_clone_instance(gconstpointer a, gconstpointer b, gpointer data_set) { int rc = 0; node_t *node1 = NULL; node_t *node2 = NULL; gboolean can1 = TRUE; gboolean can2 = TRUE; const resource_t *resource1 = (const resource_t *)a; const resource_t *resource2 = (const resource_t *)b; CRM_ASSERT(resource1 != NULL); CRM_ASSERT(resource2 != NULL); /* allocation order: * - active instances * - instances running on nodes with the least copies * - active instances on nodes that can't support them or are to be fenced * - failed instances * - inactive instances */ if (resource1->running_on && resource2->running_on) { if (g_list_length(resource1->running_on) < g_list_length(resource2->running_on)) { crm_trace("%s < %s: running_on", resource1->id, resource2->id); return -1; } else if (g_list_length(resource1->running_on) > g_list_length(resource2->running_on)) { crm_trace("%s > %s: running_on", resource1->id, resource2->id); return 1; } } if (resource1->running_on) { node1 = resource1->running_on->data; } if (resource2->running_on) { node2 = resource2->running_on->data; } if (node1) { node_t *match = pe_hash_table_lookup(resource1->allowed_nodes, node1->details->id); if (match == NULL || match->weight < 0) { crm_trace("%s: current location is unavailable", resource1->id); node1 = NULL; can1 = FALSE; } } if (node2) { node_t *match = pe_hash_table_lookup(resource2->allowed_nodes, node2->details->id); if (match == NULL || match->weight < 0) { crm_trace("%s: current location is unavailable", resource2->id); node2 = NULL; can2 = FALSE; } } if (can1 != can2) { if (can1) { crm_trace("%s < %s: availability of current location", resource1->id, resource2->id); return -1; } crm_trace("%s > %s: availability of current location", resource1->id, resource2->id); return 1; } if (resource1->priority < resource2->priority) { crm_trace("%s < %s: priority", resource1->id, resource2->id); return 1; } else if (resource1->priority > resource2->priority) { crm_trace("%s > %s: priority", resource1->id, resource2->id); return -1; } if (node1 == NULL && node2 == NULL) { crm_trace("%s == %s: not active", resource1->id, resource2->id); return 0; } if (node1 != node2) { if (node1 == NULL) { crm_trace("%s > %s: active", resource1->id, resource2->id); return 1; } else if (node2 == NULL) { crm_trace("%s < %s: active", resource1->id, resource2->id); return -1; } } can1 = can_run_resources(node1); can2 = can_run_resources(node2); if (can1 != can2) { if (can1) { crm_trace("%s < %s: can", resource1->id, resource2->id); return -1; } crm_trace("%s > %s: can", resource1->id, resource2->id); return 1; } node1 = parent_node_instance(resource1, node1); node2 = parent_node_instance(resource2, node2); if (node1 != NULL && node2 == NULL) { crm_trace("%s < %s: not allowed", resource1->id, resource2->id); return -1; } else if (node1 == NULL && node2 != NULL) { crm_trace("%s > %s: not allowed", resource1->id, resource2->id); return 1; } if (node1 == NULL || node2 == NULL) { crm_trace("%s == %s: not allowed", resource1->id, resource2->id); return 0; } if (node1->count < node2->count) { crm_trace("%s < %s: count", resource1->id, resource2->id); return -1; } else if (node1->count > node2->count) { crm_trace("%s > %s: count", resource1->id, resource2->id); return 1; } can1 = did_fail(resource1); can2 = did_fail(resource2); if (can1 != can2) { if (can1) { crm_trace("%s > %s: failed", resource1->id, resource2->id); return 1; } crm_trace("%s < %s: failed", resource1->id, resource2->id); return -1; } if (node1 && node2) { int lpc = 0; int max = 0; node_t *n = NULL; GListPtr gIter = NULL; GListPtr list1 = NULL; GListPtr list2 = NULL; GHashTable *hash1 = g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, g_hash_destroy_str); GHashTable *hash2 = g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, g_hash_destroy_str); n = node_copy(resource1->running_on->data); g_hash_table_insert(hash1, (gpointer) n->details->id, n); n = node_copy(resource2->running_on->data); g_hash_table_insert(hash2, (gpointer) n->details->id, n); if(resource1->parent) { for (gIter = resource1->parent->rsc_cons; gIter; gIter = gIter->next) { rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data; crm_trace("Applying %s to %s", constraint->id, resource1->id); hash1 = native_merge_weights(constraint->rsc_rh, resource1->id, hash1, constraint->node_attribute, (float)constraint->score / INFINITY, 0); } for (gIter = resource1->parent->rsc_cons_lhs; gIter; gIter = gIter->next) { rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data; crm_trace("Applying %s to %s", constraint->id, resource1->id); hash1 = native_merge_weights(constraint->rsc_lh, resource1->id, hash1, constraint->node_attribute, (float)constraint->score / INFINITY, pe_weights_positive); } } if(resource2->parent) { for (gIter = resource2->parent->rsc_cons; gIter; gIter = gIter->next) { rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data; crm_trace("Applying %s to %s", constraint->id, resource2->id); hash2 = native_merge_weights(constraint->rsc_rh, resource2->id, hash2, constraint->node_attribute, (float)constraint->score / INFINITY, 0); } for (gIter = resource2->parent->rsc_cons_lhs; gIter; gIter = gIter->next) { rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data; crm_trace("Applying %s to %s", constraint->id, resource2->id); hash2 = native_merge_weights(constraint->rsc_lh, resource2->id, hash2, constraint->node_attribute, (float)constraint->score / INFINITY, pe_weights_positive); } } /* Current location score */ node1 = g_list_nth_data(resource1->running_on, 0); node1 = g_hash_table_lookup(hash1, node1->details->id); node2 = g_list_nth_data(resource2->running_on, 0); node2 = g_hash_table_lookup(hash2, node2->details->id); if (node1->weight < node2->weight) { if (node1->weight < 0) { crm_trace("%s > %s: current score: %d %d", resource1->id, resource2->id, node1->weight, node2->weight); rc = -1; goto out; } else { crm_trace("%s < %s: current score: %d %d", resource1->id, resource2->id, node1->weight, node2->weight); rc = 1; goto out; } } else if (node1->weight > node2->weight) { crm_trace("%s > %s: current score: %d %d", resource1->id, resource2->id, node1->weight, node2->weight); rc = -1; goto out; } /* All location scores */ list1 = g_hash_table_get_values(hash1); list2 = g_hash_table_get_values(hash2); list1 = g_list_sort_with_data(list1, sort_node_weight, g_list_nth_data(resource1->running_on, 0)); list2 = g_list_sort_with_data(list2, sort_node_weight, g_list_nth_data(resource2->running_on, 0)); max = g_list_length(list1); if (max < g_list_length(list2)) { max = g_list_length(list2); } for (; lpc < max; lpc++) { node1 = g_list_nth_data(list1, lpc); node2 = g_list_nth_data(list2, lpc); if (node1 == NULL) { crm_trace("%s < %s: colocated score NULL", resource1->id, resource2->id); rc = 1; break; } else if (node2 == NULL) { crm_trace("%s > %s: colocated score NULL", resource1->id, resource2->id); rc = -1; break; } if (node1->weight < node2->weight) { crm_trace("%s < %s: colocated score", resource1->id, resource2->id); rc = 1; break; } else if (node1->weight > node2->weight) { crm_trace("%s > %s: colocated score", resource1->id, resource2->id); rc = -1; break; } } /* Order by reverse uname - same as sort_node_weight() does? */ out: g_hash_table_destroy(hash1); /* Free mem */ g_hash_table_destroy(hash2); /* Free mem */ g_list_free(list1); g_list_free(list2); if (rc != 0) { return rc; } } rc = strcmp(resource1->id, resource2->id); crm_trace("%s %c %s: default", resource1->id, rc < 0 ? '<' : '>', resource2->id); return rc; } static node_t * can_run_instance(resource_t * rsc, node_t * node, int limit) { node_t *local_node = NULL; if (node == NULL && rsc->allowed_nodes) { GHashTableIter iter; g_hash_table_iter_init(&iter, rsc->allowed_nodes); while (g_hash_table_iter_next(&iter, NULL, (void **)&local_node)) { can_run_instance(rsc, local_node, limit); } return NULL; } if (can_run_resources(node) == FALSE) { goto bail; } else if (is_set(rsc->flags, pe_rsc_orphan)) { goto bail; } local_node = parent_node_instance(rsc, node); if (local_node == NULL) { crm_warn("%s cannot run on %s: node not allowed", rsc->id, node->details->uname); goto bail; } else if (local_node->weight < 0) { common_update_score(rsc, node->details->id, local_node->weight); pe_rsc_trace(rsc, "%s cannot run on %s: Parent node weight doesn't allow it.", rsc->id, node->details->uname); } else if (local_node->count < limit) { pe_rsc_trace(rsc, "%s can run on %s: %d", rsc->id, node->details->uname, local_node->count); return local_node; } else { pe_rsc_trace(rsc, "%s cannot run on %s: node full (%d >= %d)", rsc->id, node->details->uname, local_node->count, limit); } bail: if (node) { common_update_score(rsc, node->details->id, -INFINITY); } return NULL; } static node_t * color_instance(resource_t * rsc, node_t * prefer, gboolean all_coloc, int limit, pe_working_set_t * data_set) { node_t *chosen = NULL; GHashTable *backup = NULL; CRM_ASSERT(rsc); pe_rsc_trace(rsc, "Processing %s %d %s", rsc->id, all_coloc, prefer?prefer->details->uname:"none"); if (is_not_set(rsc->flags, pe_rsc_provisional)) { return rsc->fns->location(rsc, NULL, FALSE); } else if (is_set(rsc->flags, pe_rsc_allocating)) { pe_rsc_debug(rsc, "Dependency loop detected involving %s", rsc->id); return NULL; } /* Only include positive colocation preferences of dependent resources * if not every node will get a copy of the clone */ append_parent_colocation(rsc->parent, rsc, all_coloc); if (prefer) { node_t *local_prefer = g_hash_table_lookup(rsc->allowed_nodes, prefer->details->id); if (local_prefer == NULL || local_prefer->weight < 0) { pe_rsc_trace(rsc, "Not pre-allocating %s to %s - unavailable", rsc->id, prefer->details->uname); return NULL; } } can_run_instance(rsc, NULL, limit); backup = node_hash_dup(rsc->allowed_nodes); chosen = rsc->cmds->allocate(rsc, prefer, data_set); if (chosen) { node_t *local_node = parent_node_instance(rsc, chosen); if (prefer && chosen && chosen->details != prefer->details) { crm_notice("Pre-allocation failed: got %s instead of %s", chosen->details->uname, prefer->details->uname); g_hash_table_destroy(rsc->allowed_nodes); rsc->allowed_nodes = backup; native_deallocate(rsc); chosen = NULL; backup = NULL; } else if (local_node) { local_node->count++; } else if (is_set(rsc->flags, pe_rsc_managed)) { /* what to do? we can't enforce per-node limits in this case */ crm_config_err("%s not found in %s (list=%d)", chosen->details->id, rsc->parent->id, g_hash_table_size(rsc->parent->allowed_nodes)); } } if(backup) { g_hash_table_destroy(backup); } return chosen; } static void append_parent_colocation(resource_t * rsc, resource_t * child, gboolean all) { GListPtr gIter = NULL; gIter = rsc->rsc_cons; for (; gIter != NULL; gIter = gIter->next) { rsc_colocation_t *cons = (rsc_colocation_t *) gIter->data; if (all || cons->score < 0 || cons->score == INFINITY) { child->rsc_cons = g_list_prepend(child->rsc_cons, cons); } } gIter = rsc->rsc_cons_lhs; for (; gIter != NULL; gIter = gIter->next) { rsc_colocation_t *cons = (rsc_colocation_t *) gIter->data; if (all || cons->score < 0) { child->rsc_cons_lhs = g_list_prepend(child->rsc_cons_lhs, cons); } } } void distribute_children(resource_t *rsc, GListPtr children, GListPtr nodes, int max, int per_host_max, pe_working_set_t * data_set); void distribute_children(resource_t *rsc, GListPtr children, GListPtr nodes, int max, int per_host_max, pe_working_set_t * data_set) { int loop_max = 0; int allocated = 0; int available_nodes = 0; /* count now tracks the number of clones currently allocated */ for(GListPtr nIter = nodes; nIter != NULL; nIter = nIter->next) { pe_node_t *node = nIter->data; node->count = 0; if (can_run_resources(node)) { available_nodes++; } } if(available_nodes) { loop_max = max / available_nodes; } if (loop_max < 1) { loop_max = 1; } pe_rsc_debug(rsc, "Allocating %d %s instances to a possible %d nodes (%d per host, %d optimal)", max, rsc->id, available_nodes, per_host_max, loop_max); /* Pre-allocate as many instances as we can to their current location */ for (GListPtr gIter = children; gIter != NULL && allocated < max; gIter = gIter->next) { resource_t *child = (resource_t *) gIter->data; if (child->running_on && is_set(child->flags, pe_rsc_provisional) && is_not_set(child->flags, pe_rsc_failed)) { node_t *child_node = child->running_on->data; node_t *local_node = parent_node_instance(child, child->running_on->data); pe_rsc_trace(rsc, "Pre-allocating %s (%d remaining)", child->id, max - allocated); pe_rsc_trace(rsc, "Foo %s to %s %d %d", child->id, child_node->details->uname, max, available_nodes); if (can_run_resources(child_node) == FALSE || child_node->weight < 0) { pe_rsc_trace(rsc, "Not Pre-allocating %s", child_node->details->uname); } else if(local_node && local_node->count >= loop_max) { pe_rsc_trace(rsc, "Deferring allocation of %s", child_node->details->uname); } else if (color_instance(child, child_node, max < available_nodes, per_host_max, data_set)) { pe_rsc_trace(rsc, "Pre-allocated %s to %s", child->id, child_node->details->uname); allocated++; } } } pe_rsc_trace(rsc, "Done pre-allocating (%d of %d)", allocated, max); for (GListPtr gIter = children; gIter != NULL; gIter = gIter->next) { resource_t *child = (resource_t *) gIter->data; if (g_list_length(child->running_on) > 0) { node_t *child_node = child->running_on->data; node_t *local_node = parent_node_instance(child, child->running_on->data); if (local_node == NULL) { crm_err("%s is running on %s which isn't allowed", child->id, child_node->details->uname); } } if (is_not_set(child->flags, pe_rsc_provisional)) { } else if (allocated >= max) { pe_rsc_debug(rsc, "Child %s not allocated - limit reached %d %d", child->id, allocated, max); resource_location(child, NULL, -INFINITY, "clone_color:limit_reached", data_set); } else { if (color_instance(child, NULL, max < available_nodes, per_host_max, data_set)) { allocated++; } } } pe_rsc_debug(rsc, "Allocated %d %s instances of a possible %d", allocated, rsc->id, max); } node_t * clone_color(resource_t * rsc, node_t * prefer, pe_working_set_t * data_set) { GListPtr nodes = NULL; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); if (is_not_set(rsc->flags, pe_rsc_provisional)) { return NULL; } else if (is_set(rsc->flags, pe_rsc_allocating)) { pe_rsc_debug(rsc, "Dependency loop detected involving %s", rsc->id); return NULL; } set_bit(rsc->flags, pe_rsc_allocating); pe_rsc_trace(rsc, "Processing %s", rsc->id); /* this information is used by sort_clone_instance() when deciding in which * order to allocate clone instances */ for (GListPtr gIter = rsc->rsc_cons; gIter != NULL; gIter = gIter->next) { rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data; pe_rsc_trace(rsc, "%s: Coloring %s first", rsc->id, constraint->rsc_rh->id); constraint->rsc_rh->cmds->allocate(constraint->rsc_rh, prefer, data_set); } for (GListPtr gIter = rsc->rsc_cons_lhs; gIter != NULL; gIter = gIter->next) { rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data; rsc->allowed_nodes = constraint->rsc_lh->cmds->merge_weights(constraint->rsc_lh, rsc->id, rsc->allowed_nodes, constraint->node_attribute, (float)constraint->score / INFINITY, (pe_weights_rollback | pe_weights_positive)); } dump_node_scores(show_scores ? 0 : scores_log_level, rsc, __FUNCTION__, rsc->allowed_nodes); nodes = g_hash_table_get_values(rsc->allowed_nodes); nodes = g_list_sort_with_data(nodes, sort_node_weight, NULL); rsc->children = g_list_sort_with_data(rsc->children, sort_clone_instance, data_set); distribute_children(rsc, rsc->children, nodes, clone_data->clone_max, clone_data->clone_node_max, data_set); g_list_free(nodes); clear_bit(rsc->flags, pe_rsc_provisional); clear_bit(rsc->flags, pe_rsc_allocating); pe_rsc_trace(rsc, "Done allocating %s", rsc->id); return NULL; } static void clone_update_pseudo_status(resource_t * rsc, gboolean * stopping, gboolean * starting, gboolean * active) { GListPtr gIter = NULL; if (rsc->children) { gIter = rsc->children; for (; gIter != NULL; gIter = gIter->next) { resource_t *child = (resource_t *) gIter->data; clone_update_pseudo_status(child, stopping, starting, active); } return; } CRM_ASSERT(active != NULL); CRM_ASSERT(starting != NULL); CRM_ASSERT(stopping != NULL); if (rsc->running_on) { *active = TRUE; } gIter = rsc->actions; for (; gIter != NULL; gIter = gIter->next) { action_t *action = (action_t *) gIter->data; if (*starting && *stopping) { return; } else if (is_set(action->flags, pe_action_optional)) { pe_rsc_trace(rsc, "Skipping optional: %s", action->uuid); continue; } else if (is_set(action->flags, pe_action_pseudo) == FALSE && is_set(action->flags, pe_action_runnable) == FALSE) { pe_rsc_trace(rsc, "Skipping unrunnable: %s", action->uuid); continue; } else if (safe_str_eq(RSC_STOP, action->task)) { pe_rsc_trace(rsc, "Stopping due to: %s", action->uuid); *stopping = TRUE; } else if (safe_str_eq(RSC_START, action->task)) { if (is_set(action->flags, pe_action_runnable) == FALSE) { pe_rsc_trace(rsc, "Skipping pseudo-op: %s run=%d, pseudo=%d", action->uuid, is_set(action->flags, pe_action_runnable), is_set(action->flags, pe_action_pseudo)); } else { pe_rsc_trace(rsc, "Starting due to: %s", action->uuid); pe_rsc_trace(rsc, "%s run=%d, pseudo=%d", action->uuid, is_set(action->flags, pe_action_runnable), is_set(action->flags, pe_action_pseudo)); *starting = TRUE; } } } } static action_t * find_rsc_action(resource_t * rsc, const char *key, gboolean active_only, GListPtr * list) { action_t *match = NULL; GListPtr possible = NULL; GListPtr active = NULL; possible = find_actions(rsc->actions, key, NULL); if (active_only) { GListPtr gIter = possible; for (; gIter != NULL; gIter = gIter->next) { action_t *op = (action_t *) gIter->data; if (is_set(op->flags, pe_action_optional) == FALSE) { active = g_list_prepend(active, op); } } if (active && g_list_length(active) == 1) { match = g_list_nth_data(active, 0); } if (list) { *list = active; active = NULL; } } else if (possible && g_list_length(possible) == 1) { match = g_list_nth_data(possible, 0); } if (list) { *list = possible; possible = NULL; } if (possible) { g_list_free(possible); } if (active) { g_list_free(active); } return match; } static void child_ordering_constraints(resource_t * rsc, pe_working_set_t * data_set) { char *key = NULL; action_t *stop = NULL; action_t *start = NULL; action_t *last_stop = NULL; action_t *last_start = NULL; GListPtr gIter = NULL; gboolean active_only = TRUE; /* change to false to get the old behavior */ clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); if (clone_data->ordered == FALSE) { return; } /* we have to maintain a consistent sorted child list when building order constraints */ rsc->children = g_list_sort(rsc->children, sort_rsc_id); for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) { resource_t *child = (resource_t *) gIter->data; key = stop_key(child); stop = find_rsc_action(child, key, active_only, NULL); free(key); key = start_key(child); start = find_rsc_action(child, key, active_only, NULL); free(key); if (stop) { if (last_stop) { /* child/child relative stop */ order_actions(stop, last_stop, pe_order_optional); } last_stop = stop; } if (start) { if (last_start) { /* child/child relative start */ order_actions(last_start, start, pe_order_optional); } last_start = start; } } } void clone_create_actions(resource_t * rsc, pe_working_set_t * data_set) { clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); clone_create_pseudo_actions(rsc, rsc->children, &clone_data->start_notify, &clone_data->stop_notify,data_set); child_ordering_constraints(rsc, data_set); } void clone_create_pseudo_actions( resource_t * rsc, GListPtr children, notify_data_t **start_notify, notify_data_t **stop_notify, pe_working_set_t * data_set) { gboolean child_active = FALSE; gboolean child_starting = FALSE; gboolean child_stopping = FALSE; gboolean allow_dependent_migrations = TRUE; action_t *stop = NULL; action_t *stopped = NULL; action_t *start = NULL; action_t *started = NULL; pe_rsc_trace(rsc, "Creating actions for %s", rsc->id); for (GListPtr gIter = children; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t *) gIter->data; gboolean starting = FALSE; gboolean stopping = FALSE; child_rsc->cmds->create_actions(child_rsc, data_set); clone_update_pseudo_status(child_rsc, &stopping, &starting, &child_active); if (stopping && starting) { allow_dependent_migrations = FALSE; } child_stopping |= stopping; child_starting |= starting; } /* start */ start = create_pseudo_resource_op(rsc, RSC_START, !child_starting, TRUE, data_set); started = create_pseudo_resource_op(rsc, RSC_STARTED, !child_starting, FALSE, data_set); started->priority = INFINITY; if (child_active || child_starting) { update_action_flags(started, pe_action_runnable, __FUNCTION__, __LINE__); } if (start_notify != NULL && *start_notify == NULL) { *start_notify = create_notification_boundaries(rsc, RSC_START, start, started, data_set); } /* stop */ stop = create_pseudo_resource_op(rsc, RSC_STOP, !child_stopping, TRUE, data_set); stopped = create_pseudo_resource_op(rsc, RSC_STOPPED, !child_stopping, TRUE, data_set); stopped->priority = INFINITY; if (allow_dependent_migrations) { update_action_flags(stop, pe_action_migrate_runnable, __FUNCTION__, __LINE__); } if (stop_notify != NULL && *stop_notify == NULL) { *stop_notify = create_notification_boundaries(rsc, RSC_STOP, stop, stopped, data_set); if (*stop_notify && *start_notify) { order_actions((*stop_notify)->post_done, (*start_notify)->pre, pe_order_optional); } } } void clone_internal_constraints(resource_t * rsc, pe_working_set_t * data_set) { resource_t *last_rsc = NULL; GListPtr gIter; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); pe_rsc_trace(rsc, "Internal constraints for %s", rsc->id); new_rsc_order(rsc, RSC_STOPPED, rsc, RSC_START, pe_order_optional, data_set); new_rsc_order(rsc, RSC_START, rsc, RSC_STARTED, pe_order_runnable_left, data_set); new_rsc_order(rsc, RSC_STOP, rsc, RSC_STOPPED, pe_order_runnable_left, data_set); if (rsc->variant == pe_master) { new_rsc_order(rsc, RSC_DEMOTED, rsc, RSC_STOP, pe_order_optional, data_set); new_rsc_order(rsc, RSC_STARTED, rsc, RSC_PROMOTE, pe_order_runnable_left, data_set); } if (clone_data->ordered) { /* we have to maintain a consistent sorted child list when building order constraints */ rsc->children = g_list_sort(rsc->children, sort_rsc_id); } for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t *) gIter->data; child_rsc->cmds->internal_constraints(child_rsc, data_set); order_start_start(rsc, child_rsc, pe_order_runnable_left | pe_order_implies_first_printed); new_rsc_order(child_rsc, RSC_START, rsc, RSC_STARTED, pe_order_implies_then_printed, data_set); if (clone_data->ordered && last_rsc) { order_start_start(last_rsc, child_rsc, pe_order_optional); } order_stop_stop(rsc, child_rsc, pe_order_implies_first_printed); new_rsc_order(child_rsc, RSC_STOP, rsc, RSC_STOPPED, pe_order_implies_then_printed, data_set); if (clone_data->ordered && last_rsc) { order_stop_stop(child_rsc, last_rsc, pe_order_optional); } last_rsc = child_rsc; } } bool assign_node(resource_t * rsc, node_t * node, gboolean force) { bool changed = FALSE; if (rsc->children) { for (GListPtr gIter = rsc->children; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t *) gIter->data; changed |= assign_node(child_rsc, node, force); } return changed; } if (rsc->allocated_to != NULL) { changed = true; } native_assign_node(rsc, NULL, node, force); return changed; } static resource_t * find_compatible_child_by_node(resource_t * local_child, node_t * local_node, resource_t * rsc, - enum rsc_role_e filter, gboolean current) + GListPtr children, enum rsc_role_e filter, gboolean current) { GListPtr gIter = NULL; if (local_node == NULL) { crm_err("Can't colocate unrunnable child %s with %s", local_child->id, rsc->id); return NULL; } crm_trace("Looking for compatible child from %s for %s on %s", local_child->id, rsc->id, local_node->details->uname); - gIter = rsc->children; - for (; gIter != NULL; gIter = gIter->next) { + for (gIter = children; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t *) gIter->data; if(is_child_compatible(child_rsc, local_node, filter, current)) { crm_trace("Pairing %s with %s on %s", local_child->id, child_rsc->id, local_node->details->uname); return child_rsc; } } crm_trace("Can't pair %s with %s", local_child->id, rsc->id); return NULL; } gboolean is_child_compatible(resource_t *child_rsc, node_t * local_node, enum rsc_role_e filter, gboolean current) { node_t *node = NULL; enum rsc_role_e next_role = child_rsc->fns->state(child_rsc, current); if (is_set_recursive(child_rsc, pe_rsc_block, TRUE) == FALSE) { /* We only want instances that haven't failed */ node = child_rsc->fns->location(child_rsc, NULL, current); } if (filter != RSC_ROLE_UNKNOWN && next_role != filter) { crm_trace("Filtered %s", child_rsc->id); return FALSE; } if (node && local_node && node->details == local_node->details) { return TRUE; } else if (node) { crm_trace("%s - %s vs %s", child_rsc->id, node->details->uname, local_node->details->uname); } else { crm_trace("%s - not allocated %d", child_rsc->id, current); } return FALSE; } resource_t * -find_compatible_child(resource_t * local_child, resource_t * rsc, enum rsc_role_e filter, - gboolean current) +find_compatible_child(resource_t * local_child, resource_t * rsc, GListPtr children, enum rsc_role_e filter, gboolean current) { resource_t *pair = NULL; GListPtr gIter = NULL; GListPtr scratch = NULL; node_t *local_node = NULL; local_node = local_child->fns->location(local_child, NULL, current); if (local_node) { - return find_compatible_child_by_node(local_child, local_node, rsc, filter, current); + return find_compatible_child_by_node(local_child, local_node, rsc, children, filter, current); } scratch = g_hash_table_get_values(local_child->allowed_nodes); scratch = g_list_sort_with_data(scratch, sort_node_weight, NULL); gIter = scratch; for (; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t *) gIter->data; - pair = find_compatible_child_by_node(local_child, node, rsc, filter, current); + pair = find_compatible_child_by_node(local_child, node, rsc, children, filter, current); if (pair) { goto done; } } pe_rsc_debug(rsc, "Can't pair %s with %s", local_child->id, rsc->id); done: g_list_free(scratch); return pair; } void clone_rsc_colocation_lh(resource_t * rsc_lh, resource_t * rsc_rh, rsc_colocation_t * constraint) { /* -- Never called -- * * Instead we add the colocation constraints to the child and call from there */ CRM_ASSERT(FALSE); } void clone_rsc_colocation_rh(resource_t * rsc_lh, resource_t * rsc_rh, rsc_colocation_t * constraint) { GListPtr gIter = NULL; gboolean do_interleave = FALSE; clone_variant_data_t *clone_data = NULL; clone_variant_data_t *clone_data_lh = NULL; CRM_CHECK(constraint != NULL, return); CRM_CHECK(rsc_lh != NULL, pe_err("rsc_lh was NULL for %s", constraint->id); return); CRM_CHECK(rsc_rh != NULL, pe_err("rsc_rh was NULL for %s", constraint->id); return); CRM_CHECK(rsc_lh->variant == pe_native, return); get_clone_variant_data(clone_data, constraint->rsc_rh); pe_rsc_trace(rsc_rh, "Processing constraint %s: %s -> %s %d", constraint->id, rsc_lh->id, rsc_rh->id, constraint->score); if (pe_rsc_is_clone(constraint->rsc_lh)) { get_clone_variant_data(clone_data_lh, constraint->rsc_lh); if (clone_data_lh->interleave && clone_data->clone_node_max != clone_data_lh->clone_node_max) { crm_config_err("Cannot interleave " XML_CIB_TAG_INCARNATION " %s and %s because" " they do not support the same number of" " resources per node", constraint->rsc_lh->id, constraint->rsc_rh->id); /* only the LHS side needs to be labeled as interleave */ } else if (clone_data_lh->interleave) { do_interleave = TRUE; } } if (is_set(rsc_rh->flags, pe_rsc_provisional)) { pe_rsc_trace(rsc_rh, "%s is still provisional", rsc_rh->id); return; } else if (do_interleave) { resource_t *rh_child = NULL; - rh_child = find_compatible_child(rsc_lh, rsc_rh, RSC_ROLE_UNKNOWN, FALSE); + rh_child = find_compatible_child(rsc_lh, rsc_rh, rsc_rh->children, RSC_ROLE_UNKNOWN, FALSE); if (rh_child) { pe_rsc_debug(rsc_rh, "Pairing %s with %s", rsc_lh->id, rh_child->id); rsc_lh->cmds->rsc_colocation_lh(rsc_lh, rh_child, constraint); } else if (constraint->score >= INFINITY) { crm_notice("Cannot pair %s with instance of %s", rsc_lh->id, rsc_rh->id); assign_node(rsc_lh, NULL, TRUE); } else { pe_rsc_debug(rsc_rh, "Cannot pair %s with instance of %s", rsc_lh->id, rsc_rh->id); } return; } else if (constraint->score >= INFINITY) { GListPtr rhs = NULL; gIter = rsc_rh->children; for (; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t *) gIter->data; node_t *chosen = child_rsc->fns->location(child_rsc, NULL, FALSE); if (chosen != NULL && is_set_recursive(child_rsc, pe_rsc_block, TRUE) == FALSE) { pe_rsc_trace(rsc_rh, "Allowing %s: %s %d", constraint->id, chosen->details->uname, chosen->weight); rhs = g_list_prepend(rhs, chosen); } } node_list_exclude(rsc_lh->allowed_nodes, rhs, FALSE); g_list_free(rhs); return; } gIter = rsc_rh->children; for (; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t *) gIter->data; child_rsc->cmds->rsc_colocation_rh(rsc_lh, child_rsc, constraint); } } static enum action_tasks clone_child_action(action_t * action) { enum action_tasks result = no_action; resource_t *child = (resource_t *) action->rsc->children->data; if (safe_str_eq(action->task, "notify") || safe_str_eq(action->task, "notified")) { /* Find the action we're notifying about instead */ int stop = 0; char *key = action->uuid; int lpc = strlen(key); for (; lpc > 0; lpc--) { if (key[lpc] == '_' && stop == 0) { stop = lpc; } else if (key[lpc] == '_') { char *task_mutable = NULL; lpc++; task_mutable = strdup(key + lpc); task_mutable[stop - lpc] = 0; crm_trace("Extracted action '%s' from '%s'", task_mutable, key); result = get_complex_task(child, task_mutable, TRUE); free(task_mutable); break; } } } else { result = get_complex_task(child, action->task, TRUE); } return result; } enum pe_action_flags -clone_action_flags(action_t * action, node_t * node) +summary_action_flags(action_t * action, GListPtr children, node_t * node) { GListPtr gIter = NULL; gboolean any_runnable = FALSE; gboolean check_runnable = TRUE; enum action_tasks task = clone_child_action(action); enum pe_action_flags flags = (pe_action_optional | pe_action_runnable | pe_action_pseudo); const char *task_s = task2text(task); - gIter = action->rsc->children; - for (; gIter != NULL; gIter = gIter->next) { + for (gIter = children; gIter != NULL; gIter = gIter->next) { action_t *child_action = NULL; resource_t *child = (resource_t *) gIter->data; - child_action = - find_first_action(child->actions, NULL, task_s, child->children ? NULL : node); - pe_rsc_trace(action->rsc, "Checking for %s in %s on %s", task_s, child->id, - node ? node->details->uname : "none"); + child_action = find_first_action(child->actions, NULL, task_s, child->children ? NULL : node); + pe_rsc_trace(action->rsc, "Checking for %s in %s on %s (%s)", task_s, child->id, + node ? node->details->uname : "none", child_action?child_action->uuid:"NA"); if (child_action) { enum pe_action_flags child_flags = child->cmds->action_flags(child_action, node); if (is_set(flags, pe_action_optional) && is_set(child_flags, pe_action_optional) == FALSE) { pe_rsc_trace(child, "%s is mandatory because of %s", action->uuid, child_action->uuid); flags = crm_clear_bit(__FUNCTION__, __LINE__, action->rsc->id, flags, pe_action_optional); pe_clear_action_bit(action, pe_action_optional); } if (is_set(child_flags, pe_action_runnable)) { any_runnable = TRUE; } } else { GListPtr gIter2 = child->actions; for (; gIter2 != NULL; gIter2 = gIter2->next) { action_t *op = (action_t *) gIter2->data; pe_rsc_trace(child, "%s on %s (%s)", op->uuid, op->node ? op->node->details->uname : "none", op->task); } } } if (check_runnable && any_runnable == FALSE) { pe_rsc_trace(action->rsc, "%s is not runnable because no children are", action->uuid); flags = crm_clear_bit(__FUNCTION__, __LINE__, action->rsc->id, flags, pe_action_runnable); if (node == NULL) { pe_clear_action_bit(action, pe_action_runnable); } } return flags; } +enum pe_action_flags +clone_action_flags(action_t * action, node_t * node) +{ + return summary_action_flags(action, action->rsc->children, node); +} + static enum pe_graph_flags clone_update_actions_interleave(action_t * first, action_t * then, node_t * node, enum pe_action_flags flags, enum pe_action_flags filter, enum pe_ordering type) { gboolean current = FALSE; resource_t *first_child = NULL; GListPtr gIter = then->rsc->children; enum pe_graph_flags changed = pe_graph_none; /*pe_graph_disable */ enum action_tasks task = clone_child_action(first); const char *first_task = task2text(task); /* Fix this - lazy */ if (crm_ends_with(first->uuid, "_stopped_0") || crm_ends_with(first->uuid, "_demoted_0")) { current = TRUE; } for (; gIter != NULL; gIter = gIter->next) { resource_t *then_child = (resource_t *) gIter->data; CRM_ASSERT(then_child != NULL); - first_child = find_compatible_child(then_child, first->rsc, RSC_ROLE_UNKNOWN, current); + first_child = find_compatible_child(then_child, first->rsc, first->rsc->children, RSC_ROLE_UNKNOWN, current); if (first_child == NULL && current) { crm_trace("Ignore"); } else if (first_child == NULL) { crm_debug("No match found for %s (%d / %s / %s)", then_child->id, current, first->uuid, then->uuid); /* Me no like this hack - but what else can we do? * * If there is no-one active or about to be active * on the same node as then_child, then they must * not be allowed to start */ if (type & (pe_order_runnable_left | pe_order_implies_then) /* Mandatory */ ) { pe_rsc_info(then->rsc, "Inhibiting %s from being active", then_child->id); if(assign_node(then_child, NULL, TRUE)) { changed |= pe_graph_updated_then; } } } else { action_t *first_action = NULL; action_t *then_action = NULL; pe_rsc_debug(then->rsc, "Pairing %s with %s", first_child->id, then_child->id); first_action = find_first_action(first_child->actions, NULL, first_task, node); then_action = find_first_action(then_child->actions, NULL, then->task, node); if (first_action == NULL) { if (is_not_set(first_child->flags, pe_rsc_orphan) && crm_str_eq(first_task, RSC_STOP, TRUE) == FALSE && crm_str_eq(first_task, RSC_DEMOTE, TRUE) == FALSE) { crm_err("Internal error: No action found for %s in %s (first)", first_task, first_child->id); } else { crm_trace("No action found for %s in %s%s (first)", first_task, first_child->id, is_set(first_child->flags, pe_rsc_orphan) ? " (ORPHAN)" : ""); } continue; } /* We're only interested if 'then' is neither stopping nor being demoted */ if (then_action == NULL) { if (is_not_set(then_child->flags, pe_rsc_orphan) && crm_str_eq(then->task, RSC_STOP, TRUE) == FALSE && crm_str_eq(then->task, RSC_DEMOTE, TRUE) == FALSE) { crm_err("Internal error: No action found for %s in %s (then)", then->task, then_child->id); } else { crm_trace("No action found for %s in %s%s (then)", then->task, then_child->id, is_set(then_child->flags, pe_rsc_orphan) ? " (ORPHAN)" : ""); } continue; } if (order_actions(first_action, then_action, type)) { crm_debug("Created constraint for %s -> %s", first_action->uuid, then_action->uuid); changed |= (pe_graph_updated_first | pe_graph_updated_then); } changed |= then_child->cmds->update_actions(first_action, then_action, node, first_child->cmds->action_flags(first_action, node), filter, type); } } return changed; } enum pe_graph_flags clone_update_actions(action_t * first, action_t * then, node_t * node, enum pe_action_flags flags, enum pe_action_flags filter, enum pe_ordering type) { const char *rsc = "none"; gboolean interleave = FALSE; enum pe_graph_flags changed = pe_graph_none; if (first->rsc != then->rsc && pe_rsc_is_clone(first->rsc) && pe_rsc_is_clone(then->rsc)) { clone_variant_data_t *clone_data = NULL; if (crm_ends_with(then->uuid, "_stop_0") || crm_ends_with(then->uuid, "_demote_0")) { get_clone_variant_data(clone_data, first->rsc); rsc = first->rsc->id; } else { get_clone_variant_data(clone_data, then->rsc); rsc = then->rsc->id; } interleave = clone_data->interleave; } crm_trace("Interleave %s -> %s: %s (based on %s)", first->uuid, then->uuid, interleave ? "yes" : "no", rsc); if (interleave) { changed = clone_update_actions_interleave(first, then, node, flags, filter, type); } else if (then->rsc) { GListPtr gIter = then->rsc->children; changed |= native_update_actions(first, then, node, flags, filter, type); for (; gIter != NULL; gIter = gIter->next) { enum pe_graph_flags child_changed = pe_graph_none; GListPtr lpc = NULL; resource_t *child = (resource_t *) gIter->data; action_t *child_action = find_first_action(child->actions, NULL, then->task, node); if (child_action) { enum pe_action_flags child_flags = child->cmds->action_flags(child_action, node); if (is_set(child_flags, pe_action_runnable)) { child_changed |= child->cmds->update_actions(first, child_action, node, flags, filter, type); } changed |= child_changed; if (child_changed & pe_graph_updated_then) { for (lpc = child_action->actions_after; lpc != NULL; lpc = lpc->next) { action_wrapper_t *other = (action_wrapper_t *) lpc->data; update_action(other->action); } } } } } return changed; } void clone_rsc_location(resource_t * rsc, rsc_to_node_t * constraint) { GListPtr gIter = rsc->children; pe_rsc_trace(rsc, "Processing location constraint %s for %s", constraint->id, rsc->id); native_rsc_location(rsc, constraint); for (; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t *) gIter->data; child_rsc->cmds->rsc_location(child_rsc, constraint); } } void clone_expand(resource_t * rsc, pe_working_set_t * data_set) { GListPtr gIter = NULL; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); gIter = rsc->actions; for (; gIter != NULL; gIter = gIter->next) { action_t *op = (action_t *) gIter->data; rsc->cmds->action_flags(op, NULL); } if (clone_data->start_notify) { collect_notification_data(rsc, TRUE, TRUE, clone_data->start_notify); expand_notification_data(clone_data->start_notify, data_set); create_notifications(rsc, clone_data->start_notify, data_set); } if (clone_data->stop_notify) { collect_notification_data(rsc, TRUE, TRUE, clone_data->stop_notify); expand_notification_data(clone_data->stop_notify, data_set); create_notifications(rsc, clone_data->stop_notify, data_set); } if (clone_data->promote_notify) { collect_notification_data(rsc, TRUE, TRUE, clone_data->promote_notify); expand_notification_data(clone_data->promote_notify, data_set); create_notifications(rsc, clone_data->promote_notify, data_set); } if (clone_data->demote_notify) { collect_notification_data(rsc, TRUE, TRUE, clone_data->demote_notify); expand_notification_data(clone_data->demote_notify, data_set); create_notifications(rsc, clone_data->demote_notify, data_set); } /* Now that the notifcations have been created we can expand the children */ gIter = rsc->children; for (; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t *) gIter->data; child_rsc->cmds->expand(child_rsc, data_set); } native_expand(rsc, data_set); /* The notifications are in the graph now, we can destroy the notify_data */ free_notification_data(clone_data->demote_notify); clone_data->demote_notify = NULL; free_notification_data(clone_data->stop_notify); clone_data->stop_notify = NULL; free_notification_data(clone_data->start_notify); clone_data->start_notify = NULL; free_notification_data(clone_data->promote_notify); clone_data->promote_notify = NULL; } node_t * rsc_known_on(resource_t * rsc, GListPtr * list) { GListPtr gIter = NULL; node_t *one = NULL; GListPtr result = NULL; if (rsc->children) { gIter = rsc->children; for (; gIter != NULL; gIter = gIter->next) { resource_t *child = (resource_t *) gIter->data; rsc_known_on(child, &result); } } else if (rsc->known_on) { result = g_hash_table_get_values(rsc->known_on); } if (result && g_list_length(result) == 1) { one = g_list_nth_data(result, 0); } if (list) { GListPtr gIter = NULL; gIter = result; for (; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t *) gIter->data; if (*list == NULL || pe_find_node_id(*list, node->details->id) == NULL) { *list = g_list_prepend(*list, node); } } } g_list_free(result); return one; } static resource_t * find_instance_on(resource_t * rsc, node_t * node) { GListPtr gIter = NULL; gIter = rsc->children; for (; gIter != NULL; gIter = gIter->next) { GListPtr gIter2 = NULL; GListPtr known_list = NULL; resource_t *child = (resource_t *) gIter->data; rsc_known_on(child, &known_list); gIter2 = known_list; for (; gIter2 != NULL; gIter2 = gIter2->next) { node_t *known = (node_t *) gIter2->data; if (node->details == known->details) { g_list_free(known_list); return child; } } g_list_free(known_list); } return NULL; } gboolean clone_create_probe(resource_t * rsc, node_t * node, action_t * complete, gboolean force, pe_working_set_t * data_set) { GListPtr gIter = NULL; gboolean any_created = FALSE; clone_variant_data_t *clone_data = NULL; CRM_ASSERT(rsc); get_clone_variant_data(clone_data, rsc); rsc->children = g_list_sort(rsc->children, sort_rsc_id); if (rsc->children == NULL) { pe_warn("Clone %s has no children", rsc->id); return FALSE; } if (rsc->exclusive_discover) { node_t *allowed = g_hash_table_lookup(rsc->allowed_nodes, node->details->id); if (allowed && allowed->rsc_discover_mode != discover_exclusive) { /* exclusive discover is enabled and this node is not marked * as a node this resource should be discovered on * * remove the node from allowed_nodes so that the * notification contains only nodes that we might ever run * on */ g_hash_table_remove(rsc->allowed_nodes, node->details->id); /* Bit of a shortcut - might as well take it */ return FALSE; } } if (is_not_set(rsc->flags, pe_rsc_unique) && clone_data->clone_node_max == 1) { /* only look for one copy */ resource_t *child = NULL; /* Try whoever we probed last time */ child = find_instance_on(rsc, node); if (child) { return child->cmds->create_probe(child, node, complete, force, data_set); } /* Try whoever we plan on starting there */ gIter = rsc->children; for (; gIter != NULL; gIter = gIter->next) { node_t *local_node = NULL; resource_t *child_rsc = (resource_t *) gIter->data; CRM_ASSERT(child_rsc); local_node = child_rsc->fns->location(child_rsc, NULL, FALSE); if (local_node == NULL) { continue; } if (local_node->details == node->details) { return child_rsc->cmds->create_probe(child_rsc, node, complete, force, data_set); } } /* Fall back to the first clone instance */ CRM_ASSERT(rsc->children); child = rsc->children->data; return child->cmds->create_probe(child, node, complete, force, data_set); } gIter = rsc->children; for (; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t *) gIter->data; if (child_rsc->cmds->create_probe(child_rsc, node, complete, force, data_set)) { any_created = TRUE; } if (any_created && is_not_set(rsc->flags, pe_rsc_unique) && clone_data->clone_node_max == 1) { /* only look for one copy (clone :0) */ break; } } return any_created; } void clone_append_meta(resource_t * rsc, xmlNode * xml) { char *name = NULL; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); name = crm_meta_name(XML_RSC_ATTR_UNIQUE); crm_xml_add(xml, name, is_set(rsc->flags, pe_rsc_unique) ? "true" : "false"); free(name); name = crm_meta_name(XML_RSC_ATTR_NOTIFY); crm_xml_add(xml, name, is_set(rsc->flags, pe_rsc_notify) ? "true" : "false"); free(name); name = crm_meta_name(XML_RSC_ATTR_INCARNATION_MAX); crm_xml_add_int(xml, name, clone_data->clone_max); free(name); name = crm_meta_name(XML_RSC_ATTR_INCARNATION_NODEMAX); crm_xml_add_int(xml, name, clone_data->clone_node_max); free(name); } GHashTable * clone_merge_weights(resource_t * rsc, const char *rhs, GHashTable * nodes, const char *attr, float factor, enum pe_weights flags) { return rsc_merge_weights(rsc, rhs, nodes, attr, factor, flags); } diff --git a/pengine/container.c b/pengine/container.c index a39aebfae7..f3ea797df8 100644 --- a/pengine/container.c +++ b/pengine/container.c @@ -1,636 +1,669 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #define VARIANT_CONTAINER 1 #include static bool is_child_container_node(container_variant_data_t *data, pe_node_t *node) { for (GListPtr gIter = data->tuples; gIter != NULL; gIter = gIter->next) { container_grouping_t *tuple = (container_grouping_t *)gIter->data; if(node->details == tuple->node->details) { return TRUE; } } return FALSE; } gint sort_clone_instance(gconstpointer a, gconstpointer b, gpointer data_set); void distribute_children(resource_t *rsc, GListPtr children, GListPtr nodes, int max, int per_host_max, pe_working_set_t * data_set); +static GListPtr get_container_list(resource_t *rsc) +{ + GListPtr containers = NULL; + container_variant_data_t *data = NULL; + + get_container_variant_data(data, rsc); + for (GListPtr gIter = data->tuples; gIter != NULL; gIter = gIter->next) { + container_grouping_t *tuple = (container_grouping_t *)gIter->data; + containers = g_list_append(containers, tuple->docker); + } + return containers; +} + node_t * container_color(resource_t * rsc, node_t * prefer, pe_working_set_t * data_set) { GListPtr containers = NULL; GListPtr nodes = NULL; container_variant_data_t *container_data = NULL; CRM_CHECK(rsc != NULL, return NULL); get_container_variant_data(container_data, rsc); set_bit(rsc->flags, pe_rsc_allocating); - - for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) { - container_grouping_t *tuple = (container_grouping_t *)gIter->data; - containers = g_list_append(containers, tuple->docker); - } + containers = get_container_list(rsc); dump_node_scores(show_scores ? 0 : scores_log_level, rsc, __FUNCTION__, rsc->allowed_nodes); nodes = g_hash_table_get_values(rsc->allowed_nodes); nodes = g_list_sort_with_data(nodes, sort_node_weight, NULL); containers = g_list_sort_with_data(containers, sort_clone_instance, data_set); distribute_children(rsc, containers, nodes, container_data->replicas, container_data->replicas_per_host, data_set); g_list_free(nodes); g_list_free(containers); for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) { container_grouping_t *tuple = (container_grouping_t *)gIter->data; CRM_ASSERT(tuple); if(tuple->ip) { tuple->ip->cmds->allocate(tuple->ip, prefer, data_set); } if(tuple->remote) { tuple->remote->cmds->allocate(tuple->remote, prefer, data_set); } // Explicitly allocate tuple->child before the container->child if(tuple->child) { pe_node_t *node = NULL; GHashTableIter iter; g_hash_table_iter_init(&iter, tuple->child->allowed_nodes); while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & node)) { if(node->details != tuple->node->details) { node->weight = -INFINITY; } else { node->weight = INFINITY; } } set_bit(tuple->child->parent->flags, pe_rsc_allocating); tuple->child->cmds->allocate(tuple->child, tuple->node, data_set); clear_bit(tuple->child->parent->flags, pe_rsc_allocating); } } if(container_data->child) { pe_node_t *node = NULL; GHashTableIter iter; g_hash_table_iter_init(&iter, container_data->child->allowed_nodes); while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & node)) { if(is_child_container_node(container_data, node)) { node->weight = 0; } else { node->weight = -INFINITY; } } container_data->child->cmds->allocate(container_data->child, prefer, data_set); } clear_bit(rsc->flags, pe_rsc_allocating); clear_bit(rsc->flags, pe_rsc_provisional); return NULL; } void container_create_actions(resource_t * rsc, pe_working_set_t * data_set) { pe_action_t *action = NULL; GListPtr containers = NULL; container_variant_data_t *container_data = NULL; CRM_CHECK(rsc != NULL, return); + containers = get_container_list(rsc); get_container_variant_data(container_data, rsc); for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) { container_grouping_t *tuple = (container_grouping_t *)gIter->data; CRM_ASSERT(tuple); if(tuple->ip) { tuple->ip->cmds->create_actions(tuple->ip, data_set); } if(tuple->docker) { tuple->docker->cmds->create_actions(tuple->docker, data_set); - containers = g_list_append(containers, tuple->docker); } if(tuple->remote) { tuple->remote->cmds->create_actions(tuple->remote, data_set); } } clone_create_pseudo_actions(rsc, containers, NULL, NULL, data_set); if(container_data->child) { container_data->child->cmds->create_actions(container_data->child, data_set); if(container_data->child->variant == pe_master) { /* promote */ action = create_pseudo_resource_op(rsc, RSC_PROMOTE, TRUE, TRUE, data_set); action = create_pseudo_resource_op(rsc, RSC_PROMOTED, TRUE, TRUE, data_set); action->priority = INFINITY; /* demote */ action = create_pseudo_resource_op(rsc, RSC_DEMOTE, TRUE, TRUE, data_set); action = create_pseudo_resource_op(rsc, RSC_DEMOTED, TRUE, TRUE, data_set); action->priority = INFINITY; } } g_list_free(containers); } void container_internal_constraints(resource_t * rsc, pe_working_set_t * data_set) { container_variant_data_t *container_data = NULL; CRM_CHECK(rsc != NULL, return); get_container_variant_data(container_data, rsc); if(container_data->child) { new_rsc_order(rsc, RSC_START, container_data->child, RSC_START, pe_order_implies_first_printed, data_set); new_rsc_order(rsc, RSC_STOP, container_data->child, RSC_STOP, pe_order_implies_first_printed, data_set); if(container_data->child->children) { new_rsc_order(container_data->child, RSC_STARTED, rsc, RSC_STARTED, pe_order_implies_then_printed, data_set); new_rsc_order(container_data->child, RSC_STOPPED, rsc, RSC_STOPPED, pe_order_implies_then_printed, data_set); } else { new_rsc_order(container_data->child, RSC_START, rsc, RSC_STARTED, pe_order_implies_then_printed, data_set); new_rsc_order(container_data->child, RSC_STOP, rsc, RSC_STOPPED, pe_order_implies_then_printed, data_set); } } for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) { container_grouping_t *tuple = (container_grouping_t *)gIter->data; CRM_ASSERT(tuple); CRM_ASSERT(tuple->docker); tuple->docker->cmds->internal_constraints(tuple->docker, data_set); order_start_start(rsc, tuple->docker, pe_order_runnable_left | pe_order_implies_first_printed); if(tuple->child) { order_stop_stop(rsc, tuple->child, pe_order_implies_first_printed); } else { order_stop_stop(rsc, tuple->docker, pe_order_implies_first_printed); new_rsc_order(tuple->docker, RSC_START, rsc, RSC_STARTED, pe_order_implies_then_printed, data_set); new_rsc_order(tuple->docker, RSC_STOP, rsc, RSC_STOPPED, pe_order_implies_then_printed, data_set); } if(tuple->ip) { tuple->ip->cmds->internal_constraints(tuple->ip, data_set); // Start ip then docker new_rsc_order(tuple->ip, RSC_START, tuple->docker, RSC_START, pe_order_runnable_left|pe_order_preserve, data_set); new_rsc_order(tuple->docker, RSC_STOP, tuple->ip, RSC_STOP, pe_order_implies_first|pe_order_preserve, data_set); rsc_colocation_new("ip-with-docker", NULL, INFINITY, tuple->ip, tuple->docker, NULL, NULL, data_set); } if(tuple->remote) { /* This handles ordering and colocating remote relative to docker * (via "resource-with-container"). Since IP is also ordered and * colocated relative to docker, we don't need to do anything * explicit here with IP. */ tuple->remote->cmds->internal_constraints(tuple->remote, data_set); } if(tuple->child) { CRM_ASSERT(tuple->remote); // Start of the remote then child is implicit in the PE's remote logic } } if(container_data->child) { container_data->child->cmds->internal_constraints(container_data->child, data_set); if(container_data->child->variant == pe_master) { master_promotion_constraints(rsc, data_set); /* child demoted before global demoted */ new_rsc_order(container_data->child, RSC_DEMOTED, rsc, RSC_DEMOTED, pe_order_implies_then_printed, data_set); /* global demote before child demote */ new_rsc_order(rsc, RSC_DEMOTE, container_data->child, RSC_DEMOTE, pe_order_implies_first_printed, data_set); /* child promoted before global promoted */ new_rsc_order(container_data->child, RSC_PROMOTED, rsc, RSC_PROMOTED, pe_order_implies_then_printed, data_set); /* global promote before child promote */ new_rsc_order(rsc, RSC_PROMOTE, container_data->child, RSC_PROMOTE, pe_order_implies_first_printed, data_set); } } else { // int type = pe_order_optional | pe_order_implies_then | pe_order_restart; // custom_action_order(rsc, generate_op_key(rsc->id, RSC_STOP, 0), NULL, // rsc, generate_op_key(rsc->id, RSC_START, 0), NULL, pe_order_optional, data_set); } } static resource_t * find_compatible_tuple_by_node(resource_t * rsc_lh, node_t * candidate, resource_t * rsc, enum rsc_role_e filter, gboolean current) { container_variant_data_t *container_data = NULL; CRM_CHECK(candidate != NULL, return NULL); get_container_variant_data(container_data, rsc); crm_trace("Looking for compatible child from %s for %s on %s", rsc_lh->id, rsc->id, candidate->details->uname); for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) { container_grouping_t *tuple = (container_grouping_t *)gIter->data; if(is_child_compatible(tuple->docker, candidate, filter, current)) { crm_trace("Pairing %s with %s on %s", rsc_lh->id, tuple->docker->id, candidate->details->uname); return tuple->docker; } } crm_trace("Can't pair %s with %s", rsc_lh->id, rsc->id); return NULL; } static resource_t * find_compatible_tuple(resource_t *rsc_lh, resource_t * rsc, enum rsc_role_e filter, gboolean current) { GListPtr scratch = NULL; resource_t *pair = NULL; node_t *active_node_lh = NULL; active_node_lh = rsc_lh->fns->location(rsc_lh, NULL, current); if (active_node_lh) { return find_compatible_tuple_by_node(rsc_lh, active_node_lh, rsc, filter, current); } scratch = g_hash_table_get_values(rsc_lh->allowed_nodes); scratch = g_list_sort_with_data(scratch, sort_node_weight, NULL); for (GListPtr gIter = scratch; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t *) gIter->data; pair = find_compatible_tuple_by_node(rsc_lh, node, rsc, filter, current); if (pair) { goto done; } } pe_rsc_debug(rsc, "Can't pair %s with %s", rsc_lh->id, rsc->id); done: g_list_free(scratch); return pair; } void container_rsc_colocation_lh(resource_t * rsc, resource_t * rsc_rh, rsc_colocation_t * constraint) { /* -- Never called -- * * Instead we add the colocation constraints to the child and call from there */ CRM_ASSERT(FALSE); } void container_rsc_colocation_rh(resource_t * rsc_lh, resource_t * rsc, rsc_colocation_t * constraint) { GListPtr allocated_rhs = NULL; container_variant_data_t *container_data = NULL; CRM_CHECK(constraint != NULL, return); CRM_CHECK(rsc_lh != NULL, pe_err("rsc_lh was NULL for %s", constraint->id); return); CRM_CHECK(rsc != NULL, pe_err("rsc was NULL for %s", constraint->id); return); if (is_set(rsc->flags, pe_rsc_provisional)) { pe_rsc_trace(rsc, "%s is still provisional", rsc->id); return; } else if(constraint->rsc_lh->variant > pe_group) { resource_t *rh_child = find_compatible_tuple(rsc_lh, rsc, RSC_ROLE_UNKNOWN, FALSE); if (rh_child) { pe_rsc_debug(rsc, "Pairing %s with %s", rsc_lh->id, rh_child->id); rsc_lh->cmds->rsc_colocation_lh(rsc_lh, rh_child, constraint); } else if (constraint->score >= INFINITY) { crm_notice("Cannot pair %s with instance of %s", rsc_lh->id, rsc->id); assign_node(rsc_lh, NULL, TRUE); } else { pe_rsc_debug(rsc, "Cannot pair %s with instance of %s", rsc_lh->id, rsc->id); } return; } get_container_variant_data(container_data, rsc); pe_rsc_trace(rsc, "Processing constraint %s: %s -> %s %d", constraint->id, rsc_lh->id, rsc->id, constraint->score); for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) { container_grouping_t *tuple = (container_grouping_t *)gIter->data; if (constraint->score < INFINITY) { tuple->docker->cmds->rsc_colocation_rh(rsc_lh, tuple->docker, constraint); } else { node_t *chosen = tuple->docker->fns->location(tuple->docker, NULL, FALSE); if (chosen != NULL && is_set_recursive(tuple->docker, pe_rsc_block, TRUE) == FALSE) { pe_rsc_trace(rsc, "Allowing %s: %s %d", constraint->id, chosen->details->uname, chosen->weight); allocated_rhs = g_list_prepend(allocated_rhs, chosen); } } } if (constraint->score >= INFINITY) { node_list_exclude(rsc_lh->allowed_nodes, allocated_rhs, FALSE); } g_list_free(allocated_rhs); } enum pe_action_flags container_action_flags(action_t * action, node_t * node) { - enum pe_action_flags flags = (pe_action_optional | pe_action_runnable | pe_action_pseudo); + enum pe_action_flags flags = 0; + container_variant_data_t *data = NULL; + + get_container_variant_data(data, action->rsc); + if(data->child) { + flags = summary_action_flags(action, data->child->children, node); + + } else { + GListPtr containers = get_container_list(action->rsc); + flags = summary_action_flags(action, containers, node); + g_list_free(containers); + } return flags; } enum pe_graph_flags container_update_actions(action_t * first, action_t * then, node_t * node, enum pe_action_flags flags, enum pe_action_flags filter, enum pe_ordering type) { gboolean current = FALSE; enum pe_graph_flags changed = pe_graph_none; - container_variant_data_t *first_data = NULL; container_variant_data_t *then_data = NULL; - + GListPtr containers = NULL; // At the point we need to force container X to stop because // resource Y needs to stop, here is where we'd implement that crm_trace("%s -> %s", first->uuid, then->uuid); if(first->rsc == NULL || then->rsc == NULL) { return changed; } else if(first->rsc->variant != then->rsc->variant) { return changed; // For now } /* Fix this - lazy */ if (crm_ends_with(first->uuid, "_stopped_0") || crm_ends_with(first->uuid, "_demoted_0")) { current = TRUE; } - get_container_variant_data(first_data, first->rsc); get_container_variant_data(then_data, then->rsc); - - if(first_data->child == NULL || then_data->child == NULL) { - return changed; // For now - } + containers = get_container_list(first->rsc); for (GListPtr gIter = then_data->tuples; gIter != NULL; gIter = gIter->next) { container_grouping_t *tuple = (container_grouping_t *)gIter->data; - resource_t *first_child = find_compatible_child(tuple->docker, first_data->child, RSC_ROLE_UNKNOWN, current); + /* We can't do the then_data->child->children trick here, + * since the node's wont match + */ + resource_t *first_child = find_compatible_child(tuple->docker, first->rsc, containers, RSC_ROLE_UNKNOWN, current); if (first_child == NULL && current) { crm_trace("Ignore"); } else if (first_child == NULL) { - crm_debug("No match found for %s (%d / %s / %s)", tuple->child->id, current, first->uuid, then->uuid); + crm_debug("No match found for %s (%d / %s / %s)", tuple->docker->id, current, first->uuid, then->uuid); /* Me no like this hack - but what else can we do? * * If there is no-one active or about to be active * on the same node as then_child, then they must * not be allowed to start */ if (type & (pe_order_runnable_left | pe_order_implies_then) /* Mandatory */ ) { - pe_rsc_info(then->rsc, "Inhibiting %s from being active", tuple->child->id); - if(assign_node(tuple->child, NULL, TRUE)) { + pe_rsc_info(then->rsc, "Inhibiting %s from being active", tuple->docker->id); + if(assign_node(tuple->docker, NULL, TRUE)) { changed |= pe_graph_updated_then; } } } else { enum action_tasks task = get_complex_task(first_child, first->task, TRUE); + + /* Potentially we might want to invovle first_data->child + * if present, however we mostly just need the "you need + * to stop" signal to flow back up the ordering chain via + * the docker resources which are always present + * + * Almost certain to break if first->task or then->task is + * promote or demote + */ pe_action_t *first_action = find_first_action(first_child->actions, NULL, task2text(task), node); - pe_action_t *then_action = find_first_action(tuple->child->actions, NULL, then->task, node); + pe_action_t *then_action = find_first_action(tuple->docker->actions, NULL, then->task, node); if (order_actions(first_action, then_action, type)) { - crm_debug("Created constraint for %s -> %s", first_action->uuid, then_action->uuid); + crm_debug("Created constraint for %s (%d) -> %s (%d) %.6x", + first_action->uuid, is_set(first_action->flags, pe_action_optional), + then_action->uuid, is_set(then_action->flags, pe_action_optional), type); changed |= (pe_graph_updated_first | pe_graph_updated_then); } - changed |= tuple->child->cmds->update_actions(first_action, then_action, node, - first_child->cmds->action_flags(first_action, node), - filter, type); + if(first_action && then_action) { + changed |= tuple->docker->cmds->update_actions(first_action, then_action, node, + first_child->cmds->action_flags(first_action, node), + filter, type); + } else { + crm_err("Nothing found either for %s (%p) or %s (%p) %s", + first_child->id, first_action, + tuple->docker->id, then_action, task2text(task)); + } } } + g_list_free(containers); return changed; } void container_rsc_location(resource_t * rsc, rsc_to_node_t * constraint) { container_variant_data_t *container_data = NULL; get_container_variant_data(container_data, rsc); pe_rsc_trace(rsc, "Processing location constraint %s for %s", constraint->id, rsc->id); native_rsc_location(rsc, constraint); for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) { container_grouping_t *tuple = (container_grouping_t *)gIter->data; if (tuple->docker) { tuple->docker->cmds->rsc_location(tuple->docker, constraint); } if(tuple->ip) { tuple->ip->cmds->rsc_location(tuple->ip, constraint); } } if(container_data->child && (constraint->role_filter == RSC_ROLE_SLAVE || constraint->role_filter == RSC_ROLE_MASTER)) { - // Translate the node into container names running on that node - crm_err("Applying constraint %s", constraint->id); container_data->child->cmds->rsc_location(container_data->child, constraint); container_data->child->rsc_location = g_list_prepend(container_data->child->rsc_location, constraint); - crm_err("Added %d location constraints to %s", g_list_length(container_data->child->rsc_location), container_data->child->id); } } void container_expand(resource_t * rsc, pe_working_set_t * data_set) { container_variant_data_t *container_data = NULL; CRM_CHECK(rsc != NULL, return); get_container_variant_data(container_data, rsc); for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) { container_grouping_t *tuple = (container_grouping_t *)gIter->data; CRM_ASSERT(tuple); if (tuple->docker && tuple->remote && tuple->docker->allocated_to && fix_remote_addr(tuple->remote)) { // REMOTE_CONTAINER_HACK: Allow remote nodes that start containers with pacemaker remote inside xmlNode *nvpair = get_xpath_object("//nvpair[@name='addr']", tuple->remote->xml, LOG_ERR); g_hash_table_replace(tuple->remote->parameters, strdup("addr"), strdup(tuple->docker->allocated_to->details->uname)); crm_xml_add(nvpair, "value", tuple->docker->allocated_to->details->uname); } if(tuple->ip) { tuple->ip->cmds->expand(tuple->ip, data_set); } if(tuple->child) { tuple->child->cmds->expand(tuple->child, data_set); } if(tuple->docker) { tuple->docker->cmds->expand(tuple->docker, data_set); } if(tuple->remote) { tuple->remote->cmds->expand(tuple->remote, data_set); } } } gboolean container_create_probe(resource_t * rsc, node_t * node, action_t * complete, gboolean force, pe_working_set_t * data_set) { bool any_created = FALSE; container_variant_data_t *container_data = NULL; CRM_CHECK(rsc != NULL, return FALSE); get_container_variant_data(container_data, rsc); for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) { container_grouping_t *tuple = (container_grouping_t *)gIter->data; CRM_ASSERT(tuple); if(tuple->ip) { any_created |= tuple->ip->cmds->create_probe(tuple->ip, node, complete, force, data_set); } if(tuple->child && node->details == tuple->node->details) { any_created |= tuple->child->cmds->create_probe(tuple->child, node, complete, force, data_set); } if(tuple->docker) { bool created = tuple->docker->cmds->create_probe(tuple->docker, node, complete, force, data_set); if(created) { any_created = TRUE; /* If we're limited to one replica per host (due to * the lack of an IP range probably), then we don't * want any of our peer containers starting until * we've established that no other copies are already * running. * * Partly this is to ensure that replicas_per_host is * observed, but also to ensure that the containers * don't fail to start because the necessary port * mappings (which won't include an IP for uniqueness) * are already taken */ for (GListPtr tIter = container_data->tuples; tIter != NULL && container_data->replicas_per_host == 1; tIter = tIter->next) { container_grouping_t *other = (container_grouping_t *)tIter->data; if ((other != tuple) && (other != NULL) && (other->docker != NULL)) { custom_action_order(tuple->docker, generate_op_key(tuple->docker->id, RSC_STATUS, 0), NULL, other->docker, generate_op_key(other->docker->id, RSC_START, 0), NULL, pe_order_optional, data_set); } } } } if(FALSE && tuple->remote) { // TODO: Needed? any_created |= tuple->remote->cmds->create_probe(tuple->remote, node, complete, force, data_set); } } return any_created; } void container_append_meta(resource_t * rsc, xmlNode * xml) { } GHashTable * container_merge_weights(resource_t * rsc, const char *rhs, GHashTable * nodes, const char *attr, float factor, enum pe_weights flags) { return rsc_merge_weights(rsc, rhs, nodes, attr, factor, flags); } void container_LogActions( resource_t * rsc, pe_working_set_t * data_set, gboolean terminal) { container_variant_data_t *container_data = NULL; CRM_CHECK(rsc != NULL, return); get_container_variant_data(container_data, rsc); for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) { container_grouping_t *tuple = (container_grouping_t *)gIter->data; CRM_ASSERT(tuple); if(tuple->ip) { LogActions(tuple->ip, data_set, terminal); } if(tuple->docker) { LogActions(tuple->docker, data_set, terminal); } if(tuple->remote) { LogActions(tuple->remote, data_set, terminal); } if(tuple->child) { LogActions(tuple->child, data_set, terminal); } } } diff --git a/pengine/graph.c b/pengine/graph.c index c93745b6f7..b774c71208 100644 --- a/pengine/graph.c +++ b/pengine/graph.c @@ -1,1648 +1,1653 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include void update_colo_start_chain(action_t * action); gboolean rsc_update_action(action_t * first, action_t * then, enum pe_ordering type); static enum pe_action_flags get_action_flags(action_t * action, node_t * node) { enum pe_action_flags flags = action->flags; if (action->rsc) { flags = action->rsc->cmds->action_flags(action, NULL); if (pe_rsc_is_clone(action->rsc) && node) { /* We only care about activity on $node */ enum pe_action_flags clone_flags = action->rsc->cmds->action_flags(action, node); /* Go to great lengths to ensure the correct value for pe_action_runnable... * * If we are a clone, then for _ordering_ constraints, it's only relevant * if we are runnable _anywhere_. * * This only applies to _runnable_ though, and only for ordering constraints. * If this function is ever used during colocation, then we'll need additional logic * * Not very satisfying, but it's logical and appears to work well. */ if (is_not_set(clone_flags, pe_action_runnable) && is_set(flags, pe_action_runnable)) { pe_rsc_trace(action->rsc, "Fixing up runnable flag for %s", action->uuid); set_bit(clone_flags, pe_action_runnable); } flags = clone_flags; } } return flags; } static char * convert_non_atomic_uuid(char *old_uuid, resource_t * rsc, gboolean allow_notify, gboolean free_original) { int interval = 0; char *uuid = NULL; char *rid = NULL; char *raw_task = NULL; int task = no_action; CRM_ASSERT(rsc); pe_rsc_trace(rsc, "Processing %s", old_uuid); if (old_uuid == NULL) { return NULL; } else if (strstr(old_uuid, "notify") != NULL) { goto done; /* no conversion */ } else if (rsc->variant < pe_group) { goto done; /* no conversion */ } CRM_ASSERT(parse_op_key(old_uuid, &rid, &raw_task, &interval)); if (interval > 0) { goto done; /* no conversion */ } task = text2task(raw_task); switch (task) { case stop_rsc: case start_rsc: case action_notify: case action_promote: case action_demote: break; case stopped_rsc: case started_rsc: case action_notified: case action_promoted: case action_demoted: task--; break; case monitor_rsc: case shutdown_crm: case stonith_node: task = no_action; break; default: crm_err("Unknown action: %s", raw_task); task = no_action; break; } if (task != no_action) { if (is_set(rsc->flags, pe_rsc_notify) && allow_notify) { uuid = generate_notify_key(rid, "confirmed-post", task2text(task + 1)); } else { uuid = generate_op_key(rid, task2text(task + 1), 0); } pe_rsc_trace(rsc, "Converted %s -> %s", old_uuid, uuid); } done: if (uuid == NULL) { uuid = strdup(old_uuid); } if (free_original) { free(old_uuid); } free(raw_task); free(rid); return uuid; } static action_t * rsc_expand_action(action_t * action) { gboolean notify = FALSE; action_t *result = action; resource_t *rsc = action->rsc; if (rsc == NULL) { return action; } if ((rsc->parent == NULL) || (pe_rsc_is_clone(rsc) && (rsc->parent->variant == pe_container))) { /* Only outermost resources have notification actions. * The exception is those in bundles. */ notify = is_set(rsc->flags, pe_rsc_notify); } if (rsc->variant >= pe_group) { /* Expand 'start' -> 'started' */ char *uuid = NULL; uuid = convert_non_atomic_uuid(action->uuid, rsc, notify, FALSE); if (uuid) { pe_rsc_trace(rsc, "Converting %s to %s %d", action->uuid, uuid, is_set(rsc->flags, pe_rsc_notify)); result = find_first_action(rsc->actions, uuid, NULL, NULL); if (result == NULL) { crm_err("Couldn't expand %s to %s in %s", action->uuid, uuid, rsc->id); result = action; } free(uuid); } } return result; } static enum pe_graph_flags graph_update_action(action_t * first, action_t * then, node_t * node, enum pe_action_flags first_flags, enum pe_action_flags then_flags, enum pe_ordering type) { enum pe_graph_flags changed = pe_graph_none; gboolean processed = FALSE; /* TODO: Do as many of these in parallel as possible */ if (type & pe_order_implies_then) { processed = TRUE; if (then->rsc) { changed |= then->rsc->cmds->update_actions(first, then, node, first_flags & pe_action_optional, pe_action_optional, pe_order_implies_then); } else if (is_set(first_flags, pe_action_optional) == FALSE) { if (update_action_flags(then, pe_action_optional | pe_action_clear, __FUNCTION__, __LINE__)) { changed |= pe_graph_updated_then; } } if (changed) { pe_rsc_trace(then->rsc, "implies right: %s then %s: changed", first->uuid, then->uuid); } else { crm_trace("implies right: %s then %s %p", first->uuid, then->uuid, then->rsc); } } if ((type & pe_order_restart) && then->rsc) { enum pe_action_flags restart = (pe_action_optional | pe_action_runnable); processed = TRUE; changed |= then->rsc->cmds->update_actions(first, then, node, first_flags, restart, pe_order_restart); if (changed) { pe_rsc_trace(then->rsc, "restart: %s then %s: changed", first->uuid, then->uuid); } else { crm_trace("restart: %s then %s", first->uuid, then->uuid); } } if (type & pe_order_implies_first) { processed = TRUE; if (first->rsc) { changed |= first->rsc->cmds->update_actions(first, then, node, first_flags, pe_action_optional, pe_order_implies_first); } else if (is_set(first_flags, pe_action_optional) == FALSE) { - pe_rsc_trace(first->rsc, "first unrunnable: %s then %s", first->uuid, then->uuid); + pe_rsc_trace(first->rsc, "first unrunnable: %s (%d) then %s (%d)", + first->uuid, is_set(first_flags, pe_action_optional), + then->uuid, is_set(then_flags, pe_action_optional)); if (update_action_flags(first, pe_action_runnable | pe_action_clear, __FUNCTION__, __LINE__)) { changed |= pe_graph_updated_first; } } if (changed) { pe_rsc_trace(then->rsc, "implies left: %s then %s: changed", first->uuid, then->uuid); } else { - crm_trace("implies left: %s then %s", first->uuid, then->uuid); + crm_trace("implies left: %s (%d) then %s (%d)", + first->uuid, is_set(first_flags, pe_action_optional), + then->uuid, is_set(then_flags, pe_action_optional)); } } if (type & pe_order_implies_first_master) { processed = TRUE; if (then->rsc) { changed |= then->rsc->cmds->update_actions(first, then, node, first_flags & pe_action_optional, pe_action_optional, pe_order_implies_first_master); } if (changed) { pe_rsc_trace(then->rsc, "implies left when right rsc is Master role: %s then %s: changed", first->uuid, then->uuid); } else { crm_trace("implies left when right rsc is Master role: %s then %s", first->uuid, then->uuid); } } if (type & pe_order_one_or_more) { processed = TRUE; if (then->rsc) { changed |= then->rsc->cmds->update_actions(first, then, node, first_flags, pe_action_runnable, pe_order_one_or_more); } else if (is_set(first_flags, pe_action_runnable)) { /* alright. a "first" action is considered runnable, incremente * the 'runnable_before' counter */ then->runnable_before++; /* if the runnable before count for then exceeds the required number * of "before" runnable actions... mark then as runnable */ if (then->runnable_before >= then->required_runnable_before) { if (update_action_flags(then, pe_action_runnable, __FUNCTION__, __LINE__)) { changed |= pe_graph_updated_then; } } } if (changed) { pe_rsc_trace(then->rsc, "runnable_one_or_more: %s then %s: changed", first->uuid, then->uuid); } else { crm_trace("runnable_one_or_more: %s then %s", first->uuid, then->uuid); } } if (type & pe_order_runnable_left) { processed = TRUE; if (then->rsc) { changed |= then->rsc->cmds->update_actions(first, then, node, first_flags, pe_action_runnable, pe_order_runnable_left); } else if (is_set(first_flags, pe_action_runnable) == FALSE) { pe_rsc_trace(then->rsc, "then unrunnable: %s then %s", first->uuid, then->uuid); if (update_action_flags(then, pe_action_runnable | pe_action_clear, __FUNCTION__, __LINE__)) { changed |= pe_graph_updated_then; } } if (changed) { pe_rsc_trace(then->rsc, "runnable: %s then %s: changed", first->uuid, then->uuid); } else { crm_trace("runnable: %s then %s", first->uuid, then->uuid); } } if (type & pe_order_implies_first_migratable) { processed = TRUE; if (then->rsc) { changed |= then->rsc->cmds->update_actions(first, then, node, first_flags, pe_action_optional, pe_order_implies_first_migratable); } if (changed) { pe_rsc_trace(then->rsc, "optional: %s then %s: changed", first->uuid, then->uuid); } else { crm_trace("optional: %s then %s", first->uuid, then->uuid); } } if (type & pe_order_pseudo_left) { processed = TRUE; if (then->rsc) { changed |= then->rsc->cmds->update_actions(first, then, node, first_flags, pe_action_optional, pe_order_pseudo_left); } if (changed) { pe_rsc_trace(then->rsc, "optional: %s then %s: changed", first->uuid, then->uuid); } else { crm_trace("optional: %s then %s", first->uuid, then->uuid); } } if (type & pe_order_optional) { processed = TRUE; if (then->rsc) { changed |= then->rsc->cmds->update_actions(first, then, node, first_flags, pe_action_runnable, pe_order_optional); } if (changed) { pe_rsc_trace(then->rsc, "optional: %s then %s: changed", first->uuid, then->uuid); } else { crm_trace("optional: %s then %s", first->uuid, then->uuid); } } if (type & pe_order_asymmetrical) { processed = TRUE; if (then->rsc) { changed |= then->rsc->cmds->update_actions(first, then, node, first_flags, pe_action_runnable, pe_order_asymmetrical); } if (changed) { pe_rsc_trace(then->rsc, "asymmetrical: %s then %s: changed", first->uuid, then->uuid); } else { crm_trace("asymmetrical: %s then %s", first->uuid, then->uuid); } } if ((first->flags & pe_action_runnable) && (type & pe_order_implies_then_printed) && (first_flags & pe_action_optional) == 0) { processed = TRUE; crm_trace("%s implies %s printed", first->uuid, then->uuid); update_action_flags(then, pe_action_print_always, __FUNCTION__, __LINE__); /* don't care about changed */ } if (is_set(type, pe_order_implies_first_printed) && is_set(then_flags, pe_action_optional) == FALSE) { processed = TRUE; crm_trace("%s implies %s printed", then->uuid, first->uuid); update_action_flags(first, pe_action_print_always, __FUNCTION__, __LINE__); /* don't care about changed */ } if ((type & pe_order_implies_then || type & pe_order_implies_first || type & pe_order_restart) && first->rsc && safe_str_eq(first->task, RSC_STOP) && is_not_set(first->rsc->flags, pe_rsc_managed) && is_set(first->rsc->flags, pe_rsc_block) && is_not_set(first->flags, pe_action_runnable)) { if (update_action_flags(then, pe_action_runnable | pe_action_clear, __FUNCTION__, __LINE__)) { changed |= pe_graph_updated_then; } if (changed) { pe_rsc_trace(then->rsc, "unmanaged left: %s then %s: changed", first->uuid, then->uuid); } else { crm_trace("unmanaged left: %s then %s", first->uuid, then->uuid); } } if (processed == FALSE) { crm_trace("Constraint 0x%.6x not applicable", type); } return changed; } static void mark_start_blocked(resource_t *rsc) { GListPtr gIter = rsc->actions; for (; gIter != NULL; gIter = gIter->next) { action_t *action = (action_t *) gIter->data; if (safe_str_neq(action->task, RSC_START)) { continue; } if (is_set(action->flags, pe_action_runnable)) { clear_bit(action->flags, pe_action_runnable); update_colo_start_chain(action); update_action(action); } } } void update_colo_start_chain(action_t *action) { GListPtr gIter = NULL; resource_t *rsc = NULL; if (is_not_set(action->flags, pe_action_runnable) && safe_str_eq(action->task, RSC_START)) { rsc = uber_parent(action->rsc); } if (rsc == NULL || rsc->rsc_cons_lhs == NULL) { return; } /* if rsc has children, all the children need to have start set to * unrunnable before we follow the colo chain for the parent. */ for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) { resource_t *child = (resource_t *)gIter->data; action_t *start = find_first_action(child->actions, NULL, RSC_START, NULL); if (start == NULL || is_set(start->flags, pe_action_runnable)) { return; } } for (gIter = rsc->rsc_cons_lhs; gIter != NULL; gIter = gIter->next) { rsc_colocation_t *colocate_with = (rsc_colocation_t *)gIter->data; if (colocate_with->score == INFINITY) { mark_start_blocked(colocate_with->rsc_lh); } } } gboolean update_action(action_t * then) { GListPtr lpc = NULL; enum pe_graph_flags changed = pe_graph_none; int last_flags = then->flags; crm_trace("Processing %s (%s %s %s)", then->uuid, is_set(then->flags, pe_action_optional) ? "optional" : "required", is_set(then->flags, pe_action_runnable) ? "runnable" : "unrunnable", is_set(then->flags, pe_action_pseudo) ? "pseudo" : then->node ? then->node->details->uname : ""); if (is_set(then->flags, pe_action_requires_any)) { /* initialize current known runnable before actions to 0 * from here as graph_update_action is called for each of * then's before actions, this number will increment as * runnable 'first' actions are encountered */ then->runnable_before = 0; /* for backwards compatibility with previous options that use * the 'requires_any' flag, initialize required to 1 if it is * not set. */ if (then->required_runnable_before == 0) { then->required_runnable_before = 1; } clear_bit(then->flags, pe_action_runnable); /* We are relying on the pe_order_one_or_more clause of * graph_update_action(), called as part of the: * * 'if (first == other->action)' * * block below, to set this back if appropriate */ } for (lpc = then->actions_before; lpc != NULL; lpc = lpc->next) { action_wrapper_t *other = (action_wrapper_t *) lpc->data; action_t *first = other->action; node_t *then_node = then->node; node_t *first_node = first->node; enum pe_action_flags then_flags = 0; enum pe_action_flags first_flags = 0; if (first->rsc && first->rsc->variant == pe_group && safe_str_eq(first->task, RSC_START)) { first_node = first->rsc->fns->location(first->rsc, NULL, FALSE); if (first_node) { crm_trace("First: Found node %s for %s", first_node->details->uname, first->uuid); } } if (then->rsc && then->rsc->variant == pe_group && safe_str_eq(then->task, RSC_START)) { then_node = then->rsc->fns->location(then->rsc, NULL, FALSE); if (then_node) { crm_trace("Then: Found node %s for %s", then_node->details->uname, then->uuid); } } /* Disable constraint if it only applies when on same node, but isn't */ if (is_set(other->type, pe_order_same_node) && first_node && then_node && (first_node->details != then_node->details)) { crm_trace("Disabled constraint %s on %s -> %s on %s", other->action->uuid, first_node->details->uname, then->uuid, then_node->details->uname); other->type = pe_order_none; continue; } clear_bit(changed, pe_graph_updated_first); if (first->rsc && then->rsc && (first->rsc != then->rsc) && (is_parent(then->rsc, first->rsc) == FALSE)) { first = rsc_expand_action(first); } if (first != other->action) { crm_trace("Ordering %s after %s instead of %s", then->uuid, first->uuid, other->action->uuid); } first_flags = get_action_flags(first, then_node); then_flags = get_action_flags(then, first_node); crm_trace("Checking %s (%s %s %s) against %s (%s %s %s) filter=0x%.6x type=0x%.6x", then->uuid, is_set(then_flags, pe_action_optional) ? "optional" : "required", is_set(then_flags, pe_action_runnable) ? "runnable" : "unrunnable", is_set(then_flags, pe_action_pseudo) ? "pseudo" : then->node ? then->node->details-> uname : "", first->uuid, is_set(first_flags, pe_action_optional) ? "optional" : "required", is_set(first_flags, pe_action_runnable) ? "runnable" : "unrunnable", is_set(first_flags, pe_action_pseudo) ? "pseudo" : first->node ? first->node->details-> uname : "", first_flags, other->type); if (first == other->action) { /* * 'first' was not expanded (ie. from 'start' to 'running'), which could mean it: * - has no associated resource, * - was a primitive, * - was pre-expanded (ie. 'running' instead of 'start') * * The third argument here to graph_update_action() is a node which is used under two conditions: * - Interleaving, in which case first->node and * then->node are equal (and NULL) * - If 'then' is a clone, to limit the scope of the * constraint to instances on the supplied node * */ int otype = other->type; node_t *node = then->node; if(is_set(otype, pe_order_implies_then_on_node)) { /* Normally we want the _whole_ 'then' clone to * restart if 'first' is restarted, so then->node is * needed. * * However for unfencing, we want to limit this to * instances on the same node as 'first' (the * unfencing operation), so first->node is supplied. * * Swap the node, from then on we can can treat it * like any other 'pe_order_implies_then' */ clear_bit(otype, pe_order_implies_then_on_node); set_bit(otype, pe_order_implies_then); node = first->node; } clear_bit(first_flags, pe_action_pseudo); changed |= graph_update_action(first, then, node, first_flags, then_flags, otype); /* 'first' was for a complex resource (clone, group, etc), * create a new dependency if necessary */ } else if (order_actions(first, then, other->type)) { /* This was the first time 'first' and 'then' were associated, * start again to get the new actions_before list */ changed |= (pe_graph_updated_then | pe_graph_disable); } if (changed & pe_graph_disable) { - crm_trace("Disabled constraint %s -> %s", other->action->uuid, then->uuid); + crm_trace("Disabled constraint %s -> %s in favor of %s -> %s", + other->action->uuid, then->uuid, first->uuid, then->uuid); clear_bit(changed, pe_graph_disable); other->type = pe_order_none; } if (changed & pe_graph_updated_first) { GListPtr lpc2 = NULL; crm_trace("Updated %s (first %s %s %s), processing dependents ", first->uuid, is_set(first->flags, pe_action_optional) ? "optional" : "required", is_set(first->flags, pe_action_runnable) ? "runnable" : "unrunnable", is_set(first->flags, pe_action_pseudo) ? "pseudo" : first->node ? first->node->details-> uname : ""); for (lpc2 = first->actions_after; lpc2 != NULL; lpc2 = lpc2->next) { action_wrapper_t *other = (action_wrapper_t *) lpc2->data; update_action(other->action); } update_action(first); } } if (is_set(then->flags, pe_action_requires_any)) { if (last_flags != then->flags) { changed |= pe_graph_updated_then; } else { clear_bit(changed, pe_graph_updated_then); } } if (changed & pe_graph_updated_then) { crm_trace("Updated %s (then %s %s %s), processing dependents ", then->uuid, is_set(then->flags, pe_action_optional) ? "optional" : "required", is_set(then->flags, pe_action_runnable) ? "runnable" : "unrunnable", is_set(then->flags, pe_action_pseudo) ? "pseudo" : then->node ? then->node->details-> uname : ""); if (is_set(last_flags, pe_action_runnable) && is_not_set(then->flags, pe_action_runnable)) { update_colo_start_chain(then); } update_action(then); for (lpc = then->actions_after; lpc != NULL; lpc = lpc->next) { action_wrapper_t *other = (action_wrapper_t *) lpc->data; update_action(other->action); } } return FALSE; } gboolean shutdown_constraints(node_t * node, action_t * shutdown_op, pe_working_set_t * data_set) { /* add the stop to the before lists so it counts as a pre-req * for the shutdown */ GListPtr lpc = NULL; for (lpc = data_set->actions; lpc != NULL; lpc = lpc->next) { action_t *action = (action_t *) lpc->data; if (action->rsc == NULL || action->node == NULL) { continue; } else if (action->node->details != node->details) { continue; } else if (is_set(action->rsc->flags, pe_rsc_maintenance)) { pe_rsc_trace(action->rsc, "Skipping %s: maintenance mode", action->uuid); continue; } else if (node->details->maintenance) { pe_rsc_trace(action->rsc, "Skipping %s: node %s is in maintenance mode", action->uuid, node->details->uname); continue; } else if (safe_str_neq(action->task, RSC_STOP)) { continue; } else if (is_not_set(action->rsc->flags, pe_rsc_managed) && is_not_set(action->rsc->flags, pe_rsc_block)) { /* * If another action depends on this one, we may still end up blocking */ pe_rsc_trace(action->rsc, "Skipping %s: unmanaged", action->uuid); continue; } pe_rsc_trace(action->rsc, "Ordering %s before shutdown on %s", action->uuid, node->details->uname); pe_clear_action_bit(action, pe_action_optional); custom_action_order(action->rsc, NULL, action, NULL, strdup(CRM_OP_SHUTDOWN), shutdown_op, pe_order_optional | pe_order_runnable_left, data_set); } return TRUE; } /*! * \internal * \brief Order all actions appropriately relative to a fencing operation * * Ensure start operations of affected resources are ordered after fencing, * imply stop and demote operations of affected resources by marking them as * pseudo-actions, etc. * * \param[in] node Node to be fenced * \param[in] stonith_op Fencing operation * \param[in,out] data_set Working set of cluster */ gboolean stonith_constraints(node_t * node, action_t * stonith_op, pe_working_set_t * data_set) { GListPtr r = NULL; CRM_CHECK(stonith_op != NULL, return FALSE); for (r = data_set->resources; r != NULL; r = r->next) { rsc_stonith_ordering((resource_t *) r->data, stonith_op, data_set); } return TRUE; } static node_t * get_router_node(action_t *action) { node_t *began_on = NULL; node_t *ended_on = NULL; node_t *router_node = NULL; if (safe_str_eq(action->task, CRM_OP_FENCE) || is_remote_node(action->node) == FALSE) { return NULL; } CRM_ASSERT(action->node->details->remote_rsc != NULL); if (action->node->details->remote_rsc->running_on) { began_on = action->node->details->remote_rsc->running_on->data; } ended_on = action->node->details->remote_rsc->allocated_to; /* if there is only one location to choose from, * this is easy. Check for those conditions first */ if (!began_on || !ended_on) { /* remote rsc is either shutting down or starting up */ return began_on ? began_on : ended_on; } else if (began_on->details == ended_on->details) { /* remote rsc didn't move nodes. */ return began_on; } /* If we have get here, we know the remote resource * began on one node and is moving to another node. * * This means some actions will get routed through the cluster * node the connection rsc began on, and others are routed through * the cluster node the connection rsc ends up on. * * 1. stop, demote, migrate actions of resources living in the remote * node _MUST_ occur _BEFORE_ the connection can move (these actions * are all required before the remote rsc stop action can occur.) In * this case, we know these actions have to be routed through the initial * cluster node the connection resource lived on before the move takes place. * * 2. Everything else (start, promote, monitor, probe, refresh, clear failcount * delete ....) must occur after the resource starts on the node it is * moving to. */ /* 1. before connection rsc moves. */ if (safe_str_eq(action->task, "stop") || safe_str_eq(action->task, "demote") || safe_str_eq(action->task, "migrate_from") || safe_str_eq(action->task, "migrate_to")) { router_node = began_on; /* 2. after connection rsc moves. */ } else { router_node = ended_on; } return router_node; } /*! * \internal * \brief Add an XML node tag for a specified ID * * \param[in] id Node UUID to add * \param[in,out] xml Parent XML tag to add to */ static xmlNode* add_node_to_xml_by_id(const char *id, xmlNode *xml) { xmlNode *node_xml; node_xml = create_xml_node(xml, XML_CIB_TAG_NODE); crm_xml_add(node_xml, XML_ATTR_UUID, id); return node_xml; } /*! * \internal * \brief Add an XML node tag for a specified node * * \param[in] node Node to add * \param[in,out] xml XML to add node to */ static void add_node_to_xml(const node_t *node, void *xml) { add_node_to_xml_by_id(node->details->id, (xmlNode *) xml); } /*! * \internal * \brief Add XML with nodes that need an update of their maintenance state * * \param[in,out] xml Parent XML tag to add to * \param[in] data_set Working set for cluster */ static int add_maintenance_nodes(xmlNode *xml, const pe_working_set_t *data_set) { GListPtr gIter = NULL; xmlNode *maintenance = xml?create_xml_node(xml, XML_GRAPH_TAG_MAINTENANCE):NULL; int count = 0; for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t *) gIter->data; struct node_shared_s *details = node->details; if (!(is_remote_node(node))) { continue; /* just remote nodes need to know atm */ } if (details->maintenance != details->remote_maintenance) { if (maintenance) { crm_xml_add( add_node_to_xml_by_id(node->details->id, maintenance), XML_NODE_IS_MAINTENANCE, details->maintenance?"1":"0"); } count++; } } crm_trace("%s %d nodes to adjust maintenance-mode " "to transition", maintenance?"Added":"Counted", count); return count; } /*! * \internal * \brief Add pseudo action with nodes needing maintenance state update * * \param[in,out] data_set Working set for cluster */ void add_maintenance_update(pe_working_set_t *data_set) { action_t *action = NULL; if (add_maintenance_nodes(NULL, data_set)) { crm_trace("adding maintenance state update pseudo action"); action = get_pseudo_op(CRM_OP_MAINTENANCE_NODES, data_set); set_bit(action->flags, pe_action_print_always); } } /*! * \internal * \brief Add XML with nodes that an action is expected to bring down * * If a specified action is expected to bring any nodes down, add an XML block * with their UUIDs. When a node is lost, this allows the crmd to determine * whether it was expected. * * \param[in,out] xml Parent XML tag to add to * \param[in] action Action to check for downed nodes * \param[in] data_set Working set for cluster */ static void add_downed_nodes(xmlNode *xml, const action_t *action, const pe_working_set_t *data_set) { CRM_CHECK(xml && action && action->node && data_set, return); if (safe_str_eq(action->task, CRM_OP_SHUTDOWN)) { /* Shutdown makes the action's node down */ xmlNode *downed = create_xml_node(xml, XML_GRAPH_TAG_DOWNED); add_node_to_xml_by_id(action->node->details->id, downed); } else if (safe_str_eq(action->task, CRM_OP_FENCE)) { /* Fencing makes the action's node and any hosted guest nodes down */ const char *fence = g_hash_table_lookup(action->meta, "stonith_action"); if (safe_str_eq(fence, "off") || safe_str_eq(fence, "reboot")) { xmlNode *downed = create_xml_node(xml, XML_GRAPH_TAG_DOWNED); add_node_to_xml_by_id(action->node->details->id, downed); pe_foreach_guest_node(data_set, action->node, add_node_to_xml, downed); } } else if (action->rsc && action->rsc->is_remote_node && safe_str_eq(action->task, CRMD_ACTION_STOP)) { /* Stopping a remote connection resource makes connected node down, * unless it's part of a migration */ GListPtr iter; action_t *input; gboolean migrating = FALSE; for (iter = action->actions_before; iter != NULL; iter = iter->next) { input = ((action_wrapper_t *) iter->data)->action; if (input->rsc && safe_str_eq(action->rsc->id, input->rsc->id) && safe_str_eq(input->task, CRMD_ACTION_MIGRATED)) { migrating = TRUE; break; } } if (!migrating) { xmlNode *downed = create_xml_node(xml, XML_GRAPH_TAG_DOWNED); add_node_to_xml_by_id(action->rsc->id, downed); } } } static xmlNode * action2xml(action_t * action, gboolean as_input, pe_working_set_t *data_set) { gboolean needs_node_info = TRUE; gboolean needs_maintenance_info = FALSE; xmlNode *action_xml = NULL; xmlNode *args_xml = NULL; if (action == NULL) { return NULL; } if (safe_str_eq(action->task, CRM_OP_FENCE)) { /* All fences need node info; guest node fences are pseudo-events */ action_xml = create_xml_node(NULL, is_set(action->flags, pe_action_pseudo)? XML_GRAPH_TAG_PSEUDO_EVENT : XML_GRAPH_TAG_CRM_EVENT); } else if (safe_str_eq(action->task, CRM_OP_SHUTDOWN)) { action_xml = create_xml_node(NULL, XML_GRAPH_TAG_CRM_EVENT); } else if (safe_str_eq(action->task, CRM_OP_CLEAR_FAILCOUNT)) { action_xml = create_xml_node(NULL, XML_GRAPH_TAG_CRM_EVENT); } else if (safe_str_eq(action->task, CRM_OP_LRM_REFRESH)) { action_xml = create_xml_node(NULL, XML_GRAPH_TAG_CRM_EVENT); /* } else if(safe_str_eq(action->task, RSC_PROBED)) { */ /* action_xml = create_xml_node(NULL, XML_GRAPH_TAG_CRM_EVENT); */ } else if (is_set(action->flags, pe_action_pseudo)) { if (safe_str_eq(action->task, CRM_OP_MAINTENANCE_NODES)) { needs_maintenance_info = TRUE; } action_xml = create_xml_node(NULL, XML_GRAPH_TAG_PSEUDO_EVENT); needs_node_info = FALSE; } else { action_xml = create_xml_node(NULL, XML_GRAPH_TAG_RSC_OP); } crm_xml_add_int(action_xml, XML_ATTR_ID, action->id); crm_xml_add(action_xml, XML_LRM_ATTR_TASK, action->task); if (action->rsc != NULL && action->rsc->clone_name != NULL) { char *clone_key = NULL; const char *interval_s = g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL); int interval = crm_parse_int(interval_s, "0"); if (safe_str_eq(action->task, RSC_NOTIFY)) { const char *n_type = g_hash_table_lookup(action->meta, "notify_type"); const char *n_task = g_hash_table_lookup(action->meta, "notify_operation"); CRM_CHECK(n_type != NULL, crm_err("No notify type value found for %s", action->uuid)); CRM_CHECK(n_task != NULL, crm_err("No notify operation value found for %s", action->uuid)); clone_key = generate_notify_key(action->rsc->clone_name, n_type, n_task); } else if(action->cancel_task) { clone_key = generate_op_key(action->rsc->clone_name, action->cancel_task, interval); } else { clone_key = generate_op_key(action->rsc->clone_name, action->task, interval); } CRM_CHECK(clone_key != NULL, crm_err("Could not generate a key for %s", action->uuid)); crm_xml_add(action_xml, XML_LRM_ATTR_TASK_KEY, clone_key); crm_xml_add(action_xml, "internal_" XML_LRM_ATTR_TASK_KEY, action->uuid); free(clone_key); } else { crm_xml_add(action_xml, XML_LRM_ATTR_TASK_KEY, action->uuid); } if (needs_node_info && action->node != NULL) { node_t *router_node = get_router_node(action); crm_xml_add(action_xml, XML_LRM_ATTR_TARGET, action->node->details->uname); crm_xml_add(action_xml, XML_LRM_ATTR_TARGET_UUID, action->node->details->id); if (router_node) { crm_xml_add(action_xml, XML_LRM_ATTR_ROUTER_NODE, router_node->details->uname); } g_hash_table_insert(action->meta, strdup(XML_LRM_ATTR_TARGET), strdup(action->node->details->uname)); g_hash_table_insert(action->meta, strdup(XML_LRM_ATTR_TARGET_UUID), strdup(action->node->details->id)); } /* No details if this action is only being listed in the inputs section */ if (as_input) { return action_xml; } /* List affected resource */ if (action->rsc) { if (is_set(action->flags, pe_action_pseudo) == FALSE) { int lpc = 0; xmlNode *rsc_xml = create_xml_node(action_xml, crm_element_name(action->rsc->xml)); const char *attr_list[] = { XML_AGENT_ATTR_CLASS, XML_AGENT_ATTR_PROVIDER, XML_ATTR_TYPE }; if (is_set(action->rsc->flags, pe_rsc_orphan) && action->rsc->clone_name) { /* Do not use the 'instance free' name here as that * might interfere with the instance we plan to keep. * Ie. if there are more than two named /anonymous/ * instances on a given node, we need to make sure the * command goes to the right one. * * Keep this block, even when everyone is using * 'instance free' anonymous clone names - it means * we'll do the right thing if anyone toggles the * unique flag to 'off' */ crm_debug("Using orphan clone name %s instead of %s", action->rsc->id, action->rsc->clone_name); crm_xml_add(rsc_xml, XML_ATTR_ID, action->rsc->clone_name); crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->id); } else if (is_not_set(action->rsc->flags, pe_rsc_unique)) { const char *xml_id = ID(action->rsc->xml); crm_debug("Using anonymous clone name %s for %s (aka. %s)", xml_id, action->rsc->id, action->rsc->clone_name); /* ID is what we'd like client to use * ID_LONG is what they might know it as instead * * ID_LONG is only strictly needed /here/ during the * transition period until all nodes in the cluster * are running the new software /and/ have rebooted * once (meaning that they've only ever spoken to a DC * supporting this feature). * * If anyone toggles the unique flag to 'on', the * 'instance free' name will correspond to an orphan * and fall into the clause above instead */ crm_xml_add(rsc_xml, XML_ATTR_ID, xml_id); if (action->rsc->clone_name && safe_str_neq(xml_id, action->rsc->clone_name)) { crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->clone_name); } else { crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->id); } } else { CRM_ASSERT(action->rsc->clone_name == NULL); crm_xml_add(rsc_xml, XML_ATTR_ID, action->rsc->id); } for (lpc = 0; lpc < DIMOF(attr_list); lpc++) { crm_xml_add(rsc_xml, attr_list[lpc], g_hash_table_lookup(action->rsc->meta, attr_list[lpc])); } } } /* List any attributes in effect */ args_xml = create_xml_node(NULL, XML_TAG_ATTRS); crm_xml_add(args_xml, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET); g_hash_table_foreach(action->extra, hash2field, args_xml); if (action->rsc != NULL && action->node) { GHashTable *p = g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); #ifdef ENABLE_VERSIONED_ATTRS xmlNode *versioned_parameters = create_xml_node(NULL, XML_TAG_VER_ATTRS); #endif get_rsc_attributes(p, action->rsc, action->node, data_set); g_hash_table_foreach(p, hash2smartfield, args_xml); #ifdef ENABLE_VERSIONED_ATTRS pe_get_versioned_attributes(versioned_parameters, action->rsc, action->node, data_set); if (xml_has_children(versioned_parameters)) { add_node_copy(action_xml, versioned_parameters); } #endif g_hash_table_destroy(p); #ifdef ENABLE_VERSIONED_ATTRS free_xml(versioned_parameters); #endif } else if(action->rsc && action->rsc->variant <= pe_native) { g_hash_table_foreach(action->rsc->parameters, hash2smartfield, args_xml); #ifdef ENABLE_VERSIONED_ATTRS if (xml_has_children(action->rsc->versioned_parameters)) { add_node_copy(action_xml, action->rsc->versioned_parameters); } #endif } g_hash_table_foreach(action->meta, hash2metafield, args_xml); if (action->rsc != NULL) { int isolated = 0; const char *value = g_hash_table_lookup(action->rsc->meta, "external-ip"); resource_t *parent = action->rsc; while (parent != NULL) { isolated |= parent->isolation_wrapper ? 1 : 0; parent->cmds->append_meta(parent, args_xml); parent = parent->parent; } if (isolated && action->node) { char *nodeattr = crm_meta_name(XML_RSC_ATTR_ISOLATION_HOST); crm_xml_add(args_xml, nodeattr, action->node->details->uname); free(nodeattr); } if(value) { hash2smartfield((gpointer)"pcmk_external_ip", (gpointer)value, (gpointer)args_xml); } } else if (safe_str_eq(action->task, CRM_OP_FENCE) && action->node) { /* Pass the node's attributes as meta-attributes. * * @TODO: Determine whether it is still necessary to do this. It was * added in 33d99707, probably for the libfence-based implementation in * c9a90bd, which is no longer used. */ g_hash_table_foreach(action->node->details->attrs, hash2metafield, args_xml); } sorted_xml(args_xml, action_xml, FALSE); free_xml(args_xml); /* List any nodes this action is expected to make down */ if (needs_node_info && (action->node != NULL)) { add_downed_nodes(action_xml, action, data_set); } if (needs_maintenance_info) { add_maintenance_nodes(action_xml, data_set); } crm_log_xml_trace(action_xml, "dumped action"); return action_xml; } static gboolean should_dump_action(action_t * action) { CRM_CHECK(action != NULL, return FALSE); if (is_set(action->flags, pe_action_dumped)) { crm_trace("action %d (%s) was already dumped", action->id, action->uuid); return FALSE; } else if (is_set(action->flags, pe_action_pseudo) && safe_str_eq(action->task, CRM_OP_PROBED)) { GListPtr lpc = NULL; /* This is a horrible but convenient hack * * It mimimizes the number of actions with unsatisfied inputs * (ie. not included in the graph) * * This in turn, means we can be more concise when printing * aborted/incomplete graphs. * * It also makes it obvious which node is preventing * probe_complete from running (presumably because it is only * partially up) * * For these reasons we tolerate such perversions */ for (lpc = action->actions_after; lpc != NULL; lpc = lpc->next) { action_wrapper_t *wrapper = (action_wrapper_t *) lpc->data; if (is_not_set(wrapper->action->flags, pe_action_runnable)) { /* Only interested in runnable operations */ } else if (safe_str_neq(wrapper->action->task, RSC_START)) { /* Only interested in start operations */ } else if (is_set(wrapper->action->flags, pe_action_dumped)) { crm_trace("action %d (%s) dependency of %s", action->id, action->uuid, wrapper->action->uuid); return TRUE; } else if (should_dump_action(wrapper->action)) { crm_trace("action %d (%s) dependency of %s", action->id, action->uuid, wrapper->action->uuid); return TRUE; } } } if (is_set(action->flags, pe_action_runnable) == FALSE) { crm_trace("action %d (%s) was not runnable", action->id, action->uuid); return FALSE; } else if (is_set(action->flags, pe_action_optional) && is_set(action->flags, pe_action_print_always) == FALSE) { crm_trace("action %d (%s) was optional", action->id, action->uuid); return FALSE; } else if (action->rsc != NULL && is_not_set(action->rsc->flags, pe_rsc_managed)) { const char *interval = NULL; interval = g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL); /* make sure probes and recurring monitors go through */ if (safe_str_neq(action->task, RSC_STATUS) && interval == NULL) { crm_trace("action %d (%s) was for an unmanaged resource (%s)", action->id, action->uuid, action->rsc->id); return FALSE; } } if (is_set(action->flags, pe_action_pseudo) || safe_str_eq(action->task, CRM_OP_FENCE) || safe_str_eq(action->task, CRM_OP_SHUTDOWN)) { /* skip the next checks */ return TRUE; } if (action->node == NULL) { pe_err("action %d (%s) was not allocated", action->id, action->uuid); log_action(LOG_DEBUG, "Unallocated action", action, FALSE); return FALSE; } else if(is_container_remote_node(action->node) && action->node->details->remote_requires_reset == FALSE) { crm_trace("Assuming action %s for %s will be runnable", action->uuid, action->node->details->uname); } else if (action->node->details->online == FALSE) { pe_err("action %d was (%s) scheduled for offline node", action->id, action->uuid); log_action(LOG_DEBUG, "Action for offline node", action, FALSE); return FALSE; #if 0 /* but this would also affect resources that can be safely * migrated before a fencing op */ } else if (action->node->details->unclean == FALSE) { pe_err("action %d was (%s) scheduled for unclean node", action->id, action->uuid); log_action(LOG_DEBUG, "Action for unclean node", action, FALSE); return FALSE; #endif } return TRUE; } /* lowest to highest */ static gint sort_action_id(gconstpointer a, gconstpointer b) { const action_wrapper_t *action_wrapper2 = (const action_wrapper_t *)a; const action_wrapper_t *action_wrapper1 = (const action_wrapper_t *)b; if (a == NULL) { return 1; } if (b == NULL) { return -1; } if (action_wrapper1->action->id > action_wrapper2->action->id) { return -1; } if (action_wrapper1->action->id < action_wrapper2->action->id) { return 1; } return 0; } static gboolean check_dump_input(int last_action, action_t * action, action_wrapper_t * wrapper) { int type = wrapper->type; if (wrapper->state == pe_link_dumped) { return TRUE; } else if (wrapper->state == pe_link_dup) { return FALSE; } type &= ~pe_order_implies_first_printed; type &= ~pe_order_implies_then_printed; type &= ~pe_order_optional; if (is_not_set(type, pe_order_preserve) && action->rsc && action->rsc->fillers && wrapper->action->rsc && wrapper->action->node && wrapper->action->node->details->remote_rsc && (wrapper->action->node->details->remote_rsc->container == action->rsc)) { /* This prevents user-defined ordering constraints between resources * running in a guest node and the resource that defines that node. */ crm_warn("Invalid ordering constraint between %s and %s", wrapper->action->rsc->id, action->rsc->id); wrapper->type = pe_order_none; return FALSE; } if (last_action == wrapper->action->id) { crm_trace("Input (%d) %s duplicated for %s", wrapper->action->id, wrapper->action->uuid, action->uuid); wrapper->state = pe_link_dup; return FALSE; } else if (wrapper->type == pe_order_none) { crm_trace("Input (%d) %s suppressed for %s", wrapper->action->id, wrapper->action->uuid, action->uuid); return FALSE; } else if (is_set(wrapper->action->flags, pe_action_runnable) == FALSE && type == pe_order_none && safe_str_neq(wrapper->action->uuid, CRM_OP_PROBED)) { crm_trace("Input (%d) %s optional (ordering) for %s", wrapper->action->id, wrapper->action->uuid, action->uuid); return FALSE; } else if (is_set(wrapper->action->flags, pe_action_runnable) == FALSE && is_set(type, pe_order_one_or_more)) { crm_trace("Input (%d) %s optional (one-or-more) for %s", wrapper->action->id, wrapper->action->uuid, action->uuid); return FALSE; } else if (is_set(action->flags, pe_action_pseudo) && (wrapper->type & pe_order_stonith_stop)) { crm_trace("Input (%d) %s suppressed for %s", wrapper->action->id, wrapper->action->uuid, action->uuid); return FALSE; } else if ((wrapper->type & pe_order_implies_first_migratable) && (is_set(wrapper->action->flags, pe_action_runnable) == FALSE)) { return FALSE; } else if ((wrapper->type & pe_order_apply_first_non_migratable) && (is_set(wrapper->action->flags, pe_action_migrate_runnable))) { return FALSE; } else if ((wrapper->type == pe_order_optional) && crm_ends_with(wrapper->action->uuid, "_stop_0") && is_set(wrapper->action->flags, pe_action_migrate_runnable)) { /* for optional only ordering, ordering is not preserved for * a stop action that is actually involved with a migration. */ return FALSE; } else if (wrapper->type == pe_order_load) { crm_trace("check load filter %s.%s -> %s.%s", wrapper->action->uuid, wrapper->action->node ? wrapper->action->node->details->uname : "", action->uuid, action->node ? action->node->details->uname : ""); if (action->rsc && safe_str_eq(action->task, RSC_MIGRATE)) { /* Remove the orders like the following if not relevant: * "load_stopped_node2" -> "rscA_migrate_to node1" * which were created also from: pengine/native.c: MigrateRsc() * order_actions(other, then, other_w->type); */ /* For migrate_to ops, we care about where it has been * allocated to, not where the action will be executed */ if (wrapper->action->node == NULL || action->rsc->allocated_to == NULL || wrapper->action->node->details != action->rsc->allocated_to->details) { /* Check if the actions are for the same node, ignore otherwise */ crm_trace("load filter - migrate"); wrapper->type = pe_order_none; return FALSE; } } else if (wrapper->action->node == NULL || action->node == NULL || wrapper->action->node->details != action->node->details) { /* Check if the actions are for the same node, ignore otherwise */ crm_trace("load filter - node"); wrapper->type = pe_order_none; return FALSE; } else if (is_set(wrapper->action->flags, pe_action_optional)) { /* Check if the pre-req is optional, ignore if so */ crm_trace("load filter - optional"); wrapper->type = pe_order_none; return FALSE; } } else if (wrapper->type == pe_order_anti_colocation) { crm_trace("check anti-colocation filter %s.%s -> %s.%s", wrapper->action->uuid, wrapper->action->node ? wrapper->action->node->details->uname : "", action->uuid, action->node ? action->node->details->uname : ""); if (wrapper->action->node && action->node && wrapper->action->node->details != action->node->details) { /* Check if the actions are for the same node, ignore otherwise */ crm_trace("anti-colocation filter - node"); wrapper->type = pe_order_none; return FALSE; } else if (is_set(wrapper->action->flags, pe_action_optional)) { /* Check if the pre-req is optional, ignore if so */ crm_trace("anti-colocation filter - optional"); wrapper->type = pe_order_none; return FALSE; } } else if (wrapper->action->rsc && wrapper->action->rsc != action->rsc && is_set(wrapper->action->rsc->flags, pe_rsc_failed) && is_not_set(wrapper->action->rsc->flags, pe_rsc_managed) && crm_ends_with(wrapper->action->uuid, "_stop_0") && action->rsc && pe_rsc_is_clone(action->rsc)) { crm_warn("Ignoring requirement that %s complete before %s:" " unmanaged failed resources cannot prevent clone shutdown", wrapper->action->uuid, action->uuid); return FALSE; } else if (is_set(wrapper->action->flags, pe_action_dumped) || should_dump_action(wrapper->action)) { crm_trace("Input (%d) %s should be dumped for %s", wrapper->action->id, wrapper->action->uuid, action->uuid); goto dump; #if 0 } else if (is_set(wrapper->action->flags, pe_action_runnable) && is_set(wrapper->action->flags, pe_action_pseudo) && wrapper->action->rsc->variant != pe_native) { crm_crit("Input (%d) %s should be dumped for %s", wrapper->action->id, wrapper->action->uuid, action->uuid); goto dump; #endif } else if (is_set(wrapper->action->flags, pe_action_optional) == TRUE && is_set(wrapper->action->flags, pe_action_print_always) == FALSE) { crm_trace("Input (%d) %s optional for %s", wrapper->action->id, wrapper->action->uuid, action->uuid); crm_trace("Input (%d) %s n=%p p=%d r=%d o=%d a=%d f=0x%.6x", wrapper->action->id, wrapper->action->uuid, wrapper->action->node, is_set(wrapper->action->flags, pe_action_pseudo), is_set(wrapper->action->flags, pe_action_runnable), is_set(wrapper->action->flags, pe_action_optional), is_set(wrapper->action->flags, pe_action_print_always), wrapper->type); return FALSE; } dump: return TRUE; } static gboolean graph_has_loop(action_t * init_action, action_t * action, action_wrapper_t * wrapper) { GListPtr lpc = NULL; gboolean has_loop = FALSE; if (is_set(wrapper->action->flags, pe_action_tracking)) { crm_trace("Breaking tracking loop: %s.%s -> %s.%s (0x%.6x)", wrapper->action->uuid, wrapper->action->node ? wrapper->action->node->details->uname : "", action->uuid, action->node ? action->node->details->uname : "", wrapper->type); return FALSE; } if (check_dump_input(-1, action, wrapper) == FALSE) { return FALSE; } /* If there's any order like: * "rscB_stop node2"-> "load_stopped_node2" -> "rscA_migrate_to node1" * rscA is being migrated from node1 to node2, * while rscB is being migrated from node2 to node1. * There will be potential graph loop. * Break the order "load_stopped_node2" -> "rscA_migrate_to node1". */ crm_trace("Checking graph loop: %s.%s -> %s.%s (0x%.6x)", wrapper->action->uuid, wrapper->action->node ? wrapper->action->node->details->uname : "", action->uuid, action->node ? action->node->details->uname : "", wrapper->type); if (wrapper->action == init_action) { crm_debug("Found graph loop: %s.%s ->...-> %s.%s", action->uuid, action->node ? action->node->details->uname : "", init_action->uuid, init_action->node ? init_action->node->details->uname : ""); return TRUE; } set_bit(wrapper->action->flags, pe_action_tracking); for (lpc = wrapper->action->actions_before; lpc != NULL; lpc = lpc->next) { action_wrapper_t *wrapper_before = (action_wrapper_t *) lpc->data; if (graph_has_loop(init_action, wrapper->action, wrapper_before)) { has_loop = TRUE; goto done; } } done: clear_bit(wrapper->action->flags, pe_action_tracking); return has_loop; } static gboolean should_dump_input(int last_action, action_t * action, action_wrapper_t * wrapper) { wrapper->state = pe_link_not_dumped; if (check_dump_input(last_action, action, wrapper) == FALSE) { return FALSE; } if (wrapper->type == pe_order_load && action->rsc && safe_str_eq(action->task, RSC_MIGRATE)) { crm_trace("Checking graph loop - load migrate: %s.%s -> %s.%s", wrapper->action->uuid, wrapper->action->node ? wrapper->action->node->details->uname : "", action->uuid, action->node ? action->node->details->uname : ""); if (graph_has_loop(action, action, wrapper)) { /* Remove the orders like the following if they are introducing any graph loops: * "load_stopped_node2" -> "rscA_migrate_to node1" * which were created also from: pengine/native.c: MigrateRsc() * order_actions(other, then, other_w->type); */ crm_debug("Breaking graph loop - load migrate: %s.%s -> %s.%s", wrapper->action->uuid, wrapper->action->node ? wrapper->action->node->details->uname : "", action->uuid, action->node ? action->node->details->uname : ""); wrapper->type = pe_order_none; return FALSE; } } crm_trace("Input (%d) %s n=%p p=%d r=%d o=%d a=%d f=0x%.6x dumped for %s", wrapper->action->id, wrapper->action->uuid, wrapper->action->node, is_set(wrapper->action->flags, pe_action_pseudo), is_set(wrapper->action->flags, pe_action_runnable), is_set(wrapper->action->flags, pe_action_optional), is_set(wrapper->action->flags, pe_action_print_always), wrapper->type, action->uuid); return TRUE; } void graph_element_from_action(action_t * action, pe_working_set_t * data_set) { GListPtr lpc = NULL; int last_action = -1; int synapse_priority = 0; xmlNode *syn = NULL; xmlNode *set = NULL; xmlNode *in = NULL; xmlNode *input = NULL; xmlNode *xml_action = NULL; if (should_dump_action(action) == FALSE) { return; } set_bit(action->flags, pe_action_dumped); syn = create_xml_node(data_set->graph, "synapse"); set = create_xml_node(syn, "action_set"); in = create_xml_node(syn, "inputs"); crm_xml_add_int(syn, XML_ATTR_ID, data_set->num_synapse); data_set->num_synapse++; if (action->rsc != NULL) { synapse_priority = action->rsc->priority; } if (action->priority > synapse_priority) { synapse_priority = action->priority; } if (synapse_priority > 0) { crm_xml_add_int(syn, XML_CIB_ATTR_PRIORITY, synapse_priority); } xml_action = action2xml(action, FALSE, data_set); add_node_nocopy(set, crm_element_name(xml_action), xml_action); action->actions_before = g_list_sort(action->actions_before, sort_action_id); for (lpc = action->actions_before; lpc != NULL; lpc = lpc->next) { action_wrapper_t *wrapper = (action_wrapper_t *) lpc->data; if (should_dump_input(last_action, action, wrapper) == FALSE) { continue; } wrapper->state = pe_link_dumped; CRM_CHECK(last_action < wrapper->action->id,; ); last_action = wrapper->action->id; input = create_xml_node(in, "trigger"); xml_action = action2xml(wrapper->action, TRUE, data_set); add_node_nocopy(input, crm_element_name(xml_action), xml_action); } } diff --git a/pengine/master.c b/pengine/master.c index 93e5186da2..c15e740cb4 100644 --- a/pengine/master.c +++ b/pengine/master.c @@ -1,1063 +1,1060 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #define VARIANT_CLONE 1 #include extern gint sort_clone_instance(gconstpointer a, gconstpointer b, gpointer data_set); static int master_score(resource_t * rsc, node_t * node, int not_set_value); static void child_promoting_constraints(clone_variant_data_t * clone_data, enum pe_ordering type, resource_t * rsc, resource_t * child, resource_t * last, pe_working_set_t * data_set) { if (child == NULL) { if (clone_data->ordered && last != NULL) { pe_rsc_trace(rsc, "Ordered version (last node)"); /* last child promote before promoted started */ new_rsc_order(last, RSC_PROMOTE, rsc, RSC_PROMOTED, type, data_set); } return; } /* child promote before global promoted */ new_rsc_order(child, RSC_PROMOTE, rsc, RSC_PROMOTED, type, data_set); /* global promote before child promote */ new_rsc_order(rsc, RSC_PROMOTE, child, RSC_PROMOTE, type, data_set); if (clone_data->ordered) { pe_rsc_trace(rsc, "Ordered version"); if (last == NULL) { /* global promote before first child promote */ last = rsc; } /* else: child/child relative promote */ order_start_start(last, child, type); new_rsc_order(last, RSC_PROMOTE, child, RSC_PROMOTE, type, data_set); } else { pe_rsc_trace(rsc, "Un-ordered version"); } } static void child_demoting_constraints(clone_variant_data_t * clone_data, enum pe_ordering type, resource_t * rsc, resource_t * child, resource_t * last, pe_working_set_t * data_set) { if (child == NULL) { if (clone_data->ordered && last != NULL) { pe_rsc_trace(rsc, "Ordered version (last node)"); /* global demote before first child demote */ new_rsc_order(rsc, RSC_DEMOTE, last, RSC_DEMOTE, pe_order_optional, data_set); } return; } /* child demote before global demoted */ new_rsc_order(child, RSC_DEMOTE, rsc, RSC_DEMOTED, pe_order_implies_then_printed, data_set); /* global demote before child demote */ new_rsc_order(rsc, RSC_DEMOTE, child, RSC_DEMOTE, pe_order_implies_first_printed, data_set); if (clone_data->ordered && last != NULL) { pe_rsc_trace(rsc, "Ordered version"); /* child/child relative demote */ new_rsc_order(child, RSC_DEMOTE, last, RSC_DEMOTE, type, data_set); } else if (clone_data->ordered) { pe_rsc_trace(rsc, "Ordered version (1st node)"); /* first child stop before global stopped */ new_rsc_order(child, RSC_DEMOTE, rsc, RSC_DEMOTED, type, data_set); } else { pe_rsc_trace(rsc, "Un-ordered version"); } } static void master_update_pseudo_status(resource_t * rsc, gboolean * demoting, gboolean * promoting) { GListPtr gIter = NULL; if (rsc->children) { gIter = rsc->children; for (; gIter != NULL; gIter = gIter->next) { resource_t *child = (resource_t *) gIter->data; master_update_pseudo_status(child, demoting, promoting); } return; } CRM_ASSERT(demoting != NULL); CRM_ASSERT(promoting != NULL); gIter = rsc->actions; for (; gIter != NULL; gIter = gIter->next) { action_t *action = (action_t *) gIter->data; if (*promoting && *demoting) { return; } else if (is_set(action->flags, pe_action_optional)) { continue; } else if (safe_str_eq(RSC_DEMOTE, action->task)) { *demoting = TRUE; } else if (safe_str_eq(RSC_PROMOTE, action->task)) { *promoting = TRUE; } } } static void apply_master_location(resource_t *child, GListPtr location_constraints, pe_node_t *chosen) { for(GListPtr gIter = location_constraints; gIter != NULL; gIter = gIter->next) { pe_node_t *cons_node = NULL; rsc_to_node_t *cons = (rsc_to_node_t*)gIter->data; if(cons->role_filter == RSC_ROLE_MASTER) { pe_rsc_trace(child, "Applying %s to %s", cons->id, child->id); cons_node = pe_find_node_id(cons->node_list_rh, chosen->details->id); } if(cons_node != NULL) { int new_priority = merge_weights(child->priority, cons_node->weight); pe_rsc_trace(child, "\t%s[%s]: %d -> %d (%d)", child->id, cons_node->details->uname, child->priority, new_priority, cons_node->weight); - crm_err("\t%s[%s]: %d -> %d (%d)", child->id, cons_node->details->uname, - child->priority, new_priority, cons_node->weight); child->priority = new_priority; } } } static node_t * can_be_master(resource_t * rsc) { node_t *node = NULL; node_t *local_node = NULL; resource_t *parent = uber_parent(rsc); clone_variant_data_t *clone_data = NULL; #if 0 enum rsc_role_e role = RSC_ROLE_UNKNOWN; role = rsc->fns->state(rsc, FALSE); crm_info("%s role: %s", rsc->id, role2text(role)); #endif if (rsc->children) { GListPtr gIter = rsc->children; for (; gIter != NULL; gIter = gIter->next) { resource_t *child = (resource_t *) gIter->data; if (can_be_master(child) == NULL) { pe_rsc_trace(rsc, "Child %s of %s can't be promoted", child->id, rsc->id); return NULL; } } } node = rsc->fns->location(rsc, NULL, FALSE); if (node == NULL) { pe_rsc_trace(rsc, "%s cannot be master: not allocated", rsc->id); return NULL; } else if (is_not_set(rsc->flags, pe_rsc_managed)) { if (rsc->fns->state(rsc, TRUE) == RSC_ROLE_MASTER) { crm_notice("Forcing unmanaged master %s to remain promoted on %s", rsc->id, node->details->uname); } else { return NULL; } } else if (rsc->priority < 0) { pe_rsc_trace(rsc, "%s cannot be master: preference: %d", rsc->id, rsc->priority); return NULL; } else if (can_run_resources(node) == FALSE) { crm_trace("Node can't run any resources: %s", node->details->uname); return NULL; } get_clone_variant_data(clone_data, parent); local_node = pe_hash_table_lookup(parent->allowed_nodes, node->details->id); if (local_node == NULL) { crm_err("%s cannot run on %s: node not allowed", rsc->id, node->details->uname); return NULL; } else if (local_node->count < clone_data->master_node_max || is_not_set(rsc->flags, pe_rsc_managed)) { return local_node; } else { pe_rsc_trace(rsc, "%s cannot be master on %s: node full", rsc->id, node->details->uname); } return NULL; } static gint sort_master_instance(gconstpointer a, gconstpointer b, gpointer data_set) { int rc; enum rsc_role_e role1 = RSC_ROLE_UNKNOWN; enum rsc_role_e role2 = RSC_ROLE_UNKNOWN; const resource_t *resource1 = (const resource_t *)a; const resource_t *resource2 = (const resource_t *)b; CRM_ASSERT(resource1 != NULL); CRM_ASSERT(resource2 != NULL); role1 = resource1->fns->state(resource1, TRUE); role2 = resource2->fns->state(resource2, TRUE); rc = sort_rsc_index(a, b); if (rc != 0) { crm_trace("%s %c %s (index)", resource1->id, rc < 0 ? '<' : '>', resource2->id); return rc; } if (role1 > role2) { crm_trace("%s %c %s (role)", resource1->id, '<', resource2->id); return -1; } else if (role1 < role2) { crm_trace("%s %c %s (role)", resource1->id, '>', resource2->id); return 1; } return sort_clone_instance(a, b, data_set); } GHashTable * master_merge_weights(resource_t * rsc, const char *rhs, GHashTable * nodes, const char *attr, float factor, enum pe_weights flags) { return rsc_merge_weights(rsc, rhs, nodes, attr, factor, flags); } static void master_promotion_order(resource_t * rsc, pe_working_set_t * data_set) { GListPtr gIter = NULL; node_t *node = NULL; node_t *chosen = NULL; clone_variant_data_t *clone_data = NULL; char score[33]; size_t len = sizeof(score); get_clone_variant_data(clone_data, rsc); if (clone_data->merged_master_weights) { return; } clone_data->merged_master_weights = TRUE; pe_rsc_trace(rsc, "Merging weights for %s", rsc->id); set_bit(rsc->flags, pe_rsc_merging); for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) { resource_t *child = (resource_t *) gIter->data; pe_rsc_trace(rsc, "Sort index: %s = %d", child->id, child->sort_index); } dump_node_scores(LOG_DEBUG_3, rsc, "Before", rsc->allowed_nodes); gIter = rsc->children; for (; gIter != NULL; gIter = gIter->next) { resource_t *child = (resource_t *) gIter->data; chosen = child->fns->location(child, NULL, FALSE); if (chosen == NULL || child->sort_index < 0) { pe_rsc_trace(rsc, "Skipping %s", child->id); continue; } node = (node_t *) pe_hash_table_lookup(rsc->allowed_nodes, chosen->details->id); CRM_ASSERT(node != NULL); /* adds in master preferences and rsc_location.role=Master */ score2char_stack(child->sort_index, score, len); pe_rsc_trace(rsc, "Adding %s to %s from %s", score, node->details->uname, child->id); node->weight = merge_weights(child->sort_index, node->weight); } dump_node_scores(LOG_DEBUG_3, rsc, "Middle", rsc->allowed_nodes); gIter = rsc->rsc_cons; for (; gIter != NULL; gIter = gIter->next) { rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data; /* (re-)adds location preferences of resources that the * master instance should/must be colocated with */ if (constraint->role_lh == RSC_ROLE_MASTER) { enum pe_weights flags = constraint->score == INFINITY ? 0 : pe_weights_rollback; pe_rsc_trace(rsc, "RHS: %s with %s: %d", constraint->rsc_lh->id, constraint->rsc_rh->id, constraint->score); rsc->allowed_nodes = constraint->rsc_rh->cmds->merge_weights(constraint->rsc_rh, rsc->id, rsc->allowed_nodes, constraint->node_attribute, (float)constraint->score / INFINITY, flags); } } gIter = rsc->rsc_cons_lhs; for (; gIter != NULL; gIter = gIter->next) { rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data; /* (re-)adds location preferences of resource that wish to be * colocated with the master instance */ if (constraint->role_rh == RSC_ROLE_MASTER) { pe_rsc_trace(rsc, "LHS: %s with %s: %d", constraint->rsc_lh->id, constraint->rsc_rh->id, constraint->score); rsc->allowed_nodes = constraint->rsc_lh->cmds->merge_weights(constraint->rsc_lh, rsc->id, rsc->allowed_nodes, constraint->node_attribute, (float)constraint->score / INFINITY, (pe_weights_rollback | pe_weights_positive)); } } gIter = rsc->rsc_tickets; for (; gIter != NULL; gIter = gIter->next) { rsc_ticket_t *rsc_ticket = (rsc_ticket_t *) gIter->data; if (rsc_ticket->role_lh == RSC_ROLE_MASTER && (rsc_ticket->ticket->granted == FALSE || rsc_ticket->ticket->standby)) { resource_location(rsc, NULL, -INFINITY, "__stateful_without_ticket__", data_set); } } dump_node_scores(LOG_DEBUG_3, rsc, "After", rsc->allowed_nodes); /* write them back and sort */ gIter = rsc->children; for (; gIter != NULL; gIter = gIter->next) { resource_t *child = (resource_t *) gIter->data; chosen = child->fns->location(child, NULL, FALSE); if (is_not_set(child->flags, pe_rsc_managed) && child->next_role == RSC_ROLE_MASTER) { child->sort_index = INFINITY; } else if (chosen == NULL || child->sort_index < 0) { pe_rsc_trace(rsc, "%s: %d", child->id, child->sort_index); } else { node = (node_t *) pe_hash_table_lookup(rsc->allowed_nodes, chosen->details->id); CRM_ASSERT(node != NULL); child->sort_index = node->weight; } pe_rsc_trace(rsc, "Set sort index: %s = %d", child->id, child->sort_index); } rsc->children = g_list_sort_with_data(rsc->children, sort_master_instance, data_set); clear_bit(rsc->flags, pe_rsc_merging); } static gboolean filter_anonymous_instance(resource_t * rsc, node_t * node) { GListPtr rIter = NULL; char *key = clone_strip(rsc->id); resource_t *parent = uber_parent(rsc); for (rIter = parent->children; rIter; rIter = rIter->next) { resource_t *child = rIter->data; resource_t *active = parent->fns->find_rsc(child, key, node, pe_find_clone|pe_find_current); /* * Look for an active instance on $node, if there is one, only it receives the master score * Use ->find_rsc() because we might be a cloned group */ if(rsc == active) { pe_rsc_trace(rsc, "Found %s for %s active on %s: done", active->id, key, node->details->uname); free(key); return TRUE; } else if(active) { pe_rsc_trace(rsc, "Found %s for %s on %s: not %s", active->id, key, node->details->uname, rsc->id); free(key); return FALSE; } else { pe_rsc_trace(rsc, "%s on %s: not active", key, node->details->uname); } } for (rIter = parent->children; rIter; rIter = rIter->next) { resource_t *child = rIter->data; /* * We know it's not running, but any score will still count if * the instance has been probed on $node * * Again use ->find_rsc() because we might be a cloned group * and knowing that other members of the group are known here * implies nothing */ rsc = parent->fns->find_rsc(child, key, NULL, pe_find_clone); CRM_LOG_ASSERT(rsc); if(rsc) { pe_rsc_trace(rsc, "Checking %s for %s on %s", rsc->id, key, node->details->uname); if (g_hash_table_lookup(rsc->known_on, node->details->id)) { free(key); return TRUE; } } } free(key); return FALSE; } static int master_score(resource_t * rsc, node_t * node, int not_set_value) { char *attr_name; char *name = rsc->id; const char *attr_value = NULL; int score = not_set_value, len = 0; if (rsc->children) { GListPtr gIter = rsc->children; for (; gIter != NULL; gIter = gIter->next) { resource_t *child = (resource_t *) gIter->data; int c_score = master_score(child, node, not_set_value); if (score == not_set_value) { score = c_score; } else { score += c_score; } } return score; } if (node == NULL) { if (rsc->fns->state(rsc, TRUE) < RSC_ROLE_STARTED) { pe_rsc_trace(rsc, "Ignoring master score for %s: unknown state", rsc->id); return score; } } else { node_t *match = pe_find_node_id(rsc->running_on, node->details->id); node_t *known = pe_hash_table_lookup(rsc->known_on, node->details->id); if (is_not_set(rsc->flags, pe_rsc_unique) && filter_anonymous_instance(rsc, node)) { pe_rsc_trace(rsc, "Anonymous clone %s is allowed on %s", rsc->id, node->details->uname); } else if (match == NULL && known == NULL) { pe_rsc_trace(rsc, "%s (aka. %s) has been filtered on %s - ignoring", rsc->id, rsc->clone_name, node->details->uname); return score; } match = pe_hash_table_lookup(rsc->allowed_nodes, node->details->id); if (match == NULL) { return score; } else if (match->weight < 0) { pe_rsc_trace(rsc, "%s on %s has score: %d - ignoring", rsc->id, match->details->uname, match->weight); return score; } } if (rsc->clone_name) { /* Use the name the lrm knows this resource as, * since that's what crm_master would have used too */ name = rsc->clone_name; } len = 8 + strlen(name); attr_name = calloc(1, len); sprintf(attr_name, "master-%s", name); if (node) { attr_value = g_hash_table_lookup(node->details->attrs, attr_name); pe_rsc_trace(rsc, "%s: %s[%s] = %s", rsc->id, attr_name, node->details->uname, crm_str(attr_value)); } if (attr_value != NULL) { score = char2score(attr_value); } free(attr_name); return score; } #define max(a, b) achildren; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); if (clone_data->applied_master_prefs) { /* Make sure we only do this once */ return; } clone_data->applied_master_prefs = TRUE; for (; gIter != NULL; gIter = gIter->next) { GHashTableIter iter; node_t *node = NULL; resource_t *child_rsc = (resource_t *) gIter->data; g_hash_table_iter_init(&iter, child_rsc->allowed_nodes); while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) { if (can_run_resources(node) == FALSE) { /* This node will never be promoted to master, * so don't apply the master score as that may * lead to clone shuffling */ continue; } score = master_score(child_rsc, node, 0); if (score > 0) { new_score = merge_weights(node->weight, score); if (new_score != node->weight) { pe_rsc_trace(rsc, "\t%s: Updating preference for %s (%d->%d)", child_rsc->id, node->details->uname, node->weight, new_score); node->weight = new_score; } } new_score = max(child_rsc->priority, score); if (new_score != child_rsc->priority) { pe_rsc_trace(rsc, "\t%s: Updating priority (%d->%d)", child_rsc->id, child_rsc->priority, new_score); child_rsc->priority = new_score; } } } } static void set_role_slave(resource_t * rsc, gboolean current) { GListPtr gIter = rsc->children; if (current) { if (rsc->role == RSC_ROLE_STARTED) { rsc->role = RSC_ROLE_SLAVE; } } else { GListPtr allocated = NULL; rsc->fns->location(rsc, &allocated, FALSE); if (allocated) { rsc->next_role = RSC_ROLE_SLAVE; } else { rsc->next_role = RSC_ROLE_STOPPED; } g_list_free(allocated); } for (; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t *) gIter->data; set_role_slave(child_rsc, current); } } static void set_role_master(resource_t * rsc) { GListPtr gIter = rsc->children; if (rsc->next_role == RSC_ROLE_UNKNOWN) { rsc->next_role = RSC_ROLE_MASTER; } for (; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t *) gIter->data; set_role_master(child_rsc); } } node_t * master_color(resource_t * rsc, node_t * prefer, pe_working_set_t * data_set) { int promoted = 0; GListPtr gIter = NULL; GListPtr gIter2 = NULL; GHashTableIter iter; node_t *node = NULL; node_t *chosen = NULL; enum rsc_role_e next_role = RSC_ROLE_UNKNOWN; char score[33]; size_t len = sizeof(score); clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); if (is_not_set(rsc->flags, pe_rsc_provisional)) { return NULL; } else if (is_set(rsc->flags, pe_rsc_allocating)) { pe_rsc_debug(rsc, "Dependency loop detected involving %s", rsc->id); return NULL; } apply_master_prefs(rsc); clone_color(rsc, prefer, data_set); set_bit(rsc->flags, pe_rsc_allocating); /* count now tracks the number of masters allocated */ g_hash_table_iter_init(&iter, rsc->allowed_nodes); while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) { node->count = 0; } /* * assign priority */ for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) { GListPtr list = NULL; resource_t *child_rsc = (resource_t *) gIter->data; pe_rsc_trace(rsc, "Assigning priority for %s: %s", child_rsc->id, role2text(child_rsc->next_role)); if (child_rsc->fns->state(child_rsc, TRUE) == RSC_ROLE_STARTED) { set_role_slave(child_rsc, TRUE); } chosen = child_rsc->fns->location(child_rsc, &list, FALSE); if (g_list_length(list) > 1) { crm_config_err("Cannot promote non-colocated child %s", child_rsc->id); } g_list_free(list); if (chosen == NULL) { continue; } next_role = child_rsc->fns->state(child_rsc, FALSE); switch (next_role) { case RSC_ROLE_STARTED: case RSC_ROLE_UNKNOWN: CRM_CHECK(chosen != NULL, break); /* * Default to -1 if no value is set * * This allows master locations to be specified * based solely on rsc_location constraints, * but prevents anyone from being promoted if * neither a constraint nor a master-score is present */ child_rsc->priority = master_score(child_rsc, chosen, -1); break; case RSC_ROLE_SLAVE: case RSC_ROLE_STOPPED: child_rsc->priority = -INFINITY; break; case RSC_ROLE_MASTER: /* We will arrive here if we're re-creating actions after a stonith */ break; default: CRM_CHECK(FALSE /* unhandled */ , crm_err("Unknown resource role: %d for %s", next_role, child_rsc->id)); } apply_master_location(child_rsc, child_rsc->rsc_location, chosen); - crm_err("Applying %d location constraints for %s", g_list_length(rsc->rsc_location), rsc->id); apply_master_location(child_rsc, rsc->rsc_location, chosen); for (gIter2 = child_rsc->rsc_cons; gIter2 != NULL; gIter2 = gIter2->next) { rsc_colocation_t *cons = (rsc_colocation_t *) gIter2->data; child_rsc->cmds->rsc_colocation_lh(child_rsc, cons->rsc_rh, cons); } child_rsc->sort_index = child_rsc->priority; pe_rsc_trace(rsc, "Assigning priority for %s: %d", child_rsc->id, child_rsc->priority); if (next_role == RSC_ROLE_MASTER) { child_rsc->sort_index = INFINITY; } } dump_node_scores(LOG_DEBUG_3, rsc, "Pre merge", rsc->allowed_nodes); master_promotion_order(rsc, data_set); /* mark the first N as masters */ for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t *) gIter->data; score2char_stack(child_rsc->sort_index, score, len); chosen = child_rsc->fns->location(child_rsc, NULL, FALSE); if (show_scores) { fprintf(stdout, "%s promotion score on %s: %s\n", child_rsc->id, chosen ? chosen->details->uname : "none", score); } else { do_crm_log(scores_log_level, "%s promotion score on %s: %s", child_rsc->id, chosen ? chosen->details->uname : "none", score); } chosen = NULL; /* nuke 'chosen' so that we don't promote more than the * required number of instances */ if (child_rsc->sort_index < 0) { pe_rsc_trace(rsc, "Not supposed to promote child: %s", child_rsc->id); } else if (promoted < clone_data->master_max || is_not_set(rsc->flags, pe_rsc_managed)) { chosen = can_be_master(child_rsc); } pe_rsc_debug(rsc, "%s master score: %d", child_rsc->id, child_rsc->priority); if (chosen == NULL) { set_role_slave(child_rsc, FALSE); continue; } else if(child_rsc->role < RSC_ROLE_MASTER && is_set(data_set->flags, pe_flag_have_quorum) == FALSE && data_set->no_quorum_policy == no_quorum_freeze) { crm_notice("Resource %s cannot be elevated from %s to %s: no-quorum-policy=freeze", child_rsc->id, role2text(child_rsc->role), role2text(child_rsc->next_role)); set_role_slave(child_rsc, FALSE); continue; } chosen->count++; pe_rsc_info(rsc, "Promoting %s (%s %s)", child_rsc->id, role2text(child_rsc->role), chosen->details->uname); set_role_master(child_rsc); promoted++; } clone_data->masters_allocated = promoted; pe_rsc_info(rsc, "%s: Promoted %d instances of a possible %d to master", rsc->id, promoted, clone_data->master_max); clear_bit(rsc->flags, pe_rsc_provisional); clear_bit(rsc->flags, pe_rsc_allocating); return NULL; } void master_create_actions(resource_t * rsc, pe_working_set_t * data_set) { action_t *action = NULL; GListPtr gIter = rsc->children; action_t *action_complete = NULL; gboolean any_promoting = FALSE; gboolean any_demoting = FALSE; resource_t *last_promote_rsc = NULL; resource_t *last_demote_rsc = NULL; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); pe_rsc_debug(rsc, "Creating actions for %s", rsc->id); /* create actions as normal */ clone_create_actions(rsc, data_set); for (; gIter != NULL; gIter = gIter->next) { gboolean child_promoting = FALSE; gboolean child_demoting = FALSE; resource_t *child_rsc = (resource_t *) gIter->data; pe_rsc_trace(rsc, "Creating actions for %s", child_rsc->id); child_rsc->cmds->create_actions(child_rsc, data_set); master_update_pseudo_status(child_rsc, &child_demoting, &child_promoting); any_demoting = any_demoting || child_demoting; any_promoting = any_promoting || child_promoting; pe_rsc_trace(rsc, "Created actions for %s: %d %d", child_rsc->id, child_promoting, child_demoting); } /* promote */ action = create_pseudo_resource_op(rsc, RSC_PROMOTE, !any_promoting, TRUE, data_set); action_complete = create_pseudo_resource_op(rsc, RSC_PROMOTED, !any_promoting, TRUE, data_set); action_complete->priority = INFINITY; child_promoting_constraints(clone_data, pe_order_optional, rsc, NULL, last_promote_rsc, data_set); if (clone_data->promote_notify == NULL) { clone_data->promote_notify = create_notification_boundaries(rsc, RSC_PROMOTE, action, action_complete, data_set); } /* demote */ action = create_pseudo_resource_op(rsc, RSC_DEMOTE, !any_demoting, TRUE, data_set); action_complete = create_pseudo_resource_op(rsc, RSC_DEMOTED, !any_demoting, TRUE, data_set); action_complete->priority = INFINITY; child_demoting_constraints(clone_data, pe_order_optional, rsc, NULL, last_demote_rsc, data_set); if (clone_data->demote_notify == NULL) { clone_data->demote_notify = create_notification_boundaries(rsc, RSC_DEMOTE, action, action_complete, data_set); if (clone_data->promote_notify) { /* If we ever wanted groups to have notifications we'd need to move this to native_internal_constraints() one day * Requires exposing *_notify */ order_actions(clone_data->stop_notify->post_done, clone_data->promote_notify->pre, pe_order_optional); order_actions(clone_data->start_notify->post_done, clone_data->promote_notify->pre, pe_order_optional); order_actions(clone_data->demote_notify->post_done, clone_data->promote_notify->pre, pe_order_optional); order_actions(clone_data->demote_notify->post_done, clone_data->start_notify->pre, pe_order_optional); order_actions(clone_data->demote_notify->post_done, clone_data->stop_notify->pre, pe_order_optional); } } /* restore the correct priority */ gIter = rsc->children; for (; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t *) gIter->data; child_rsc->priority = rsc->priority; } } void master_promotion_constraints(resource_t * rsc, pe_working_set_t * data_set) { /* global stopped before start */ new_rsc_order(rsc, RSC_STOPPED, rsc, RSC_START, pe_order_optional, data_set); /* global stopped before promote */ new_rsc_order(rsc, RSC_STOPPED, rsc, RSC_PROMOTE, pe_order_optional, data_set); /* global demoted before start */ new_rsc_order(rsc, RSC_DEMOTED, rsc, RSC_START, pe_order_optional, data_set); /* global started before promote */ new_rsc_order(rsc, RSC_STARTED, rsc, RSC_PROMOTE, pe_order_optional, data_set); /* global demoted before stop */ new_rsc_order(rsc, RSC_DEMOTED, rsc, RSC_STOP, pe_order_optional, data_set); /* global demote before demoted */ new_rsc_order(rsc, RSC_DEMOTE, rsc, RSC_DEMOTED, pe_order_optional, data_set); /* global demoted before promote */ new_rsc_order(rsc, RSC_DEMOTED, rsc, RSC_PROMOTE, pe_order_optional, data_set); } void master_internal_constraints(resource_t * rsc, pe_working_set_t * data_set) { GListPtr gIter = rsc->children; resource_t *last_rsc = NULL; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); clone_internal_constraints(rsc, data_set); master_promotion_constraints(rsc, data_set); for (; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t *) gIter->data; /* child demote before promote */ new_rsc_order(child_rsc, RSC_DEMOTE, child_rsc, RSC_PROMOTE, pe_order_optional, data_set); child_promoting_constraints(clone_data, pe_order_optional, rsc, child_rsc, last_rsc, data_set); child_demoting_constraints(clone_data, pe_order_optional, rsc, child_rsc, last_rsc, data_set); last_rsc = child_rsc; } } static void node_hash_update_one(GHashTable * hash, node_t * other, const char *attr, int score) { GHashTableIter iter; node_t *node = NULL; const char *value = NULL; if (other == NULL) { return; } else if (attr == NULL) { attr = "#" XML_ATTR_UNAME; } value = g_hash_table_lookup(other->details->attrs, attr); g_hash_table_iter_init(&iter, hash); while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) { const char *tmp = g_hash_table_lookup(node->details->attrs, attr); if (safe_str_eq(value, tmp)) { crm_trace("%s: %d + %d", node->details->uname, node->weight, other->weight); node->weight = merge_weights(node->weight, score); } } } void master_rsc_colocation_rh(resource_t * rsc_lh, resource_t * rsc_rh, rsc_colocation_t * constraint) { GListPtr gIter = NULL; CRM_CHECK(rsc_rh != NULL, return); if (is_set(rsc_rh->flags, pe_rsc_provisional)) { return; } else if (constraint->role_rh == RSC_ROLE_UNKNOWN) { pe_rsc_trace(rsc_rh, "Handling %s as a clone colocation", constraint->id); clone_rsc_colocation_rh(rsc_lh, rsc_rh, constraint); return; } CRM_CHECK(rsc_lh != NULL, return); CRM_CHECK(rsc_lh->variant == pe_native, return); pe_rsc_trace(rsc_rh, "Processing constraint %s: %d", constraint->id, constraint->score); if (constraint->role_rh == RSC_ROLE_UNKNOWN) { gIter = rsc_rh->children; for (; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t *) gIter->data; child_rsc->cmds->rsc_colocation_rh(rsc_lh, child_rsc, constraint); } } else if (is_set(rsc_lh->flags, pe_rsc_provisional)) { GListPtr rhs = NULL; gIter = rsc_rh->children; for (; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t *) gIter->data; node_t *chosen = child_rsc->fns->location(child_rsc, NULL, FALSE); enum rsc_role_e next_role = child_rsc->fns->state(child_rsc, FALSE); pe_rsc_trace(rsc_rh, "Processing: %s", child_rsc->id); if (chosen != NULL && next_role == constraint->role_rh) { pe_rsc_trace(rsc_rh, "Applying: %s %s %s %d", child_rsc->id, role2text(next_role), chosen->details->uname, constraint->score); if (constraint->score < INFINITY) { node_hash_update_one(rsc_lh->allowed_nodes, chosen, constraint->node_attribute, constraint->score); } rhs = g_list_prepend(rhs, chosen); } } /* Only do this if it's not a master-master colocation * Doing this unconditionally would prevent the slaves from being started */ if (constraint->role_lh != RSC_ROLE_MASTER || constraint->role_rh != RSC_ROLE_MASTER) { if (constraint->score >= INFINITY) { node_list_exclude(rsc_lh->allowed_nodes, rhs, TRUE); } } g_list_free(rhs); } else if (constraint->role_lh == RSC_ROLE_MASTER) { - resource_t *rh_child = find_compatible_child(rsc_lh, rsc_rh, constraint->role_rh, FALSE); + resource_t *rh_child = find_compatible_child(rsc_lh, rsc_rh, rsc_rh->children, constraint->role_rh, FALSE); if (rh_child == NULL && constraint->score >= INFINITY) { pe_rsc_trace(rsc_lh, "%s can't be promoted %s", rsc_lh->id, constraint->id); rsc_lh->priority = -INFINITY; } else if (rh_child != NULL) { int new_priority = merge_weights(rsc_lh->priority, constraint->score); pe_rsc_debug(rsc_lh, "Applying %s to %s", constraint->id, rsc_lh->id); pe_rsc_debug(rsc_lh, "\t%s: %d->%d", rsc_lh->id, rsc_lh->priority, new_priority); rsc_lh->priority = new_priority; } } return; } void master_append_meta(resource_t * rsc, xmlNode * xml) { char *name = NULL; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); clone_append_meta(rsc, xml); name = crm_meta_name(XML_RSC_ATTR_MASTER_MAX); crm_xml_add_int(xml, name, clone_data->master_max); free(name); name = crm_meta_name(XML_RSC_ATTR_MASTER_NODEMAX); crm_xml_add_int(xml, name, clone_data->master_node_max); free(name); } diff --git a/pengine/test10/bundle-order-partial-start-2.dot b/pengine/test10/bundle-order-partial-start-2.dot index 59a8b159d3..20afbe6f99 100644 --- a/pengine/test10/bundle-order-partial-start-2.dot +++ b/pengine/test10/bundle-order-partial-start-2.dot @@ -1,51 +1,72 @@ digraph "g" { +"all_stopped" [ style=bold color="green" fontcolor="orange"] +"galera-bundle-0_monitor_60000 undercloud" [ style=bold color="green" fontcolor="black"] +"galera-bundle-0_start_0 undercloud" -> "galera-bundle-0_monitor_60000 undercloud" [ style = bold] +"galera-bundle-0_start_0 undercloud" -> "galera:0_monitor_20000 galera-bundle-0" [ style = bold] +"galera-bundle-0_start_0 undercloud" -> "galera:0_monitor_30000 galera-bundle-0" [ style = bold] +"galera-bundle-0_start_0 undercloud" -> "galera:0_start_0 galera-bundle-0" [ style = bold] +"galera-bundle-0_start_0 undercloud" [ style=bold color="green" fontcolor="black"] +"galera-bundle-0_stop_0 undercloud" -> "all_stopped" [ style = bold] +"galera-bundle-0_stop_0 undercloud" -> "galera-bundle-0_start_0 undercloud" [ style = bold] +"galera-bundle-0_stop_0 undercloud" -> "galera-bundle-docker-0_stop_0 undercloud" [ style = bold] +"galera-bundle-0_stop_0 undercloud" [ style=bold color="green" fontcolor="black"] +"galera-bundle-docker-0_monitor_60000 undercloud" [ style=bold color="green" fontcolor="black"] +"galera-bundle-docker-0_start_0 undercloud" -> "galera-bundle-0_start_0 undercloud" [ style = bold] +"galera-bundle-docker-0_start_0 undercloud" -> "galera-bundle-docker-0_monitor_60000 undercloud" [ style = bold] +"galera-bundle-docker-0_start_0 undercloud" -> "galera:0_start_0 galera-bundle-0" [ style = bold] +"galera-bundle-docker-0_start_0 undercloud" [ style=bold color="green" fontcolor="black"] +"galera-bundle-docker-0_stop_0 undercloud" -> "all_stopped" [ style = bold] +"galera-bundle-docker-0_stop_0 undercloud" -> "galera-bundle-docker-0_start_0 undercloud" [ style = bold] +"galera-bundle-docker-0_stop_0 undercloud" [ style=bold color="green" fontcolor="black"] "galera-bundle-master_running_0" -> "galera-bundle_running_0" [ style = bold] "galera-bundle-master_running_0" [ style=bold color="green" fontcolor="orange"] "galera-bundle-master_start_0" -> "galera-bundle-master_running_0" [ style = bold] "galera-bundle-master_start_0" -> "galera:0_start_0 galera-bundle-0" [ style = bold] "galera-bundle-master_start_0" [ style=bold color="green" fontcolor="orange"] "galera-bundle_running_0" [ style=bold color="green" fontcolor="orange"] +"galera-bundle_start_0" -> "galera-bundle-docker-0_start_0 undercloud" [ style = bold] "galera-bundle_start_0" -> "galera-bundle-master_start_0" [ style = bold] "galera-bundle_start_0" [ style=bold color="green" fontcolor="orange"] "galera:0_monitor_20000 galera-bundle-0" [ style=bold color="green" fontcolor="black"] "galera:0_monitor_30000 galera-bundle-0" [ style=bold color="green" fontcolor="black"] "galera:0_start_0 galera-bundle-0" -> "galera-bundle-master_running_0" [ style = bold] "galera:0_start_0 galera-bundle-0" -> "galera:0_monitor_20000 galera-bundle-0" [ style = bold] "galera:0_start_0 galera-bundle-0" -> "galera:0_monitor_30000 galera-bundle-0" [ style = bold] "galera:0_start_0 galera-bundle-0" [ style=bold color="green" fontcolor="black"] "haproxy-bundle-docker-0_monitor_0 undercloud" -> "haproxy-bundle-docker-0_start_0 undercloud" [ style = bold] "haproxy-bundle-docker-0_monitor_0 undercloud" [ style=bold color="green" fontcolor="black"] "haproxy-bundle-docker-0_monitor_60000 undercloud" [ style=bold color="green" fontcolor="black"] +"haproxy-bundle-docker-0_start_0 undercloud" -> "galera-bundle-docker-0_start_0 undercloud" [ style = bold] "haproxy-bundle-docker-0_start_0 undercloud" -> "haproxy-bundle-docker-0_monitor_60000 undercloud" [ style = bold] "haproxy-bundle-docker-0_start_0 undercloud" -> "haproxy-bundle_running_0" [ style = bold] "haproxy-bundle-docker-0_start_0 undercloud" [ style=bold color="green" fontcolor="black"] "haproxy-bundle_running_0" -> "galera-bundle_start_0" [ style = bold] "haproxy-bundle_running_0" [ style=bold color="green" fontcolor="orange"] "haproxy-bundle_start_0" -> "haproxy-bundle-docker-0_start_0 undercloud" [ style = bold] "haproxy-bundle_start_0" [ style=bold color="green" fontcolor="orange"] "rabbitmq-bundle-clone_running_0" -> "rabbitmq-bundle_running_0" [ style = bold] "rabbitmq-bundle-clone_running_0" [ style=bold color="green" fontcolor="orange"] "rabbitmq-bundle-clone_start_0" -> "rabbitmq-bundle-clone_running_0" [ style = bold] "rabbitmq-bundle-clone_start_0" -> "rabbitmq:0_start_0 rabbitmq-bundle-0" [ style = bold] "rabbitmq-bundle-clone_start_0" [ style=bold color="green" fontcolor="orange"] "rabbitmq-bundle_running_0" -> "galera-bundle_start_0" [ style = bold] "rabbitmq-bundle_running_0" [ style=bold color="green" fontcolor="orange"] "rabbitmq-bundle_start_0" -> "rabbitmq-bundle-clone_start_0" [ style = bold] "rabbitmq-bundle_start_0" [ style=bold color="green" fontcolor="orange"] "rabbitmq:0_monitor_10000 rabbitmq-bundle-0" [ style=bold color="green" fontcolor="black"] "rabbitmq:0_start_0 rabbitmq-bundle-0" -> "rabbitmq-bundle-clone_running_0" [ style = bold] "rabbitmq:0_start_0 rabbitmq-bundle-0" -> "rabbitmq:0_monitor_10000 rabbitmq-bundle-0" [ style = bold] "rabbitmq:0_start_0 rabbitmq-bundle-0" [ style=bold color="green" fontcolor="black"] "redis-bundle-master_promote_0" -> "redis_promote_0 redis-bundle-0" [ style = bold] "redis-bundle-master_promote_0" [ style=bold color="green" fontcolor="orange"] "redis-bundle-master_promoted_0" -> "redis-bundle_promoted_0" [ style = bold] "redis-bundle-master_promoted_0" [ style=bold color="green" fontcolor="orange"] "redis-bundle_promote_0" -> "redis-bundle-master_promote_0" [ style = bold] "redis-bundle_promote_0" [ style=bold color="green" fontcolor="orange"] "redis-bundle_promoted_0" -> "galera-bundle_start_0" [ style = bold] "redis-bundle_promoted_0" [ style=bold color="green" fontcolor="orange"] "redis_monitor_20000 redis-bundle-0" [ style=bold color="green" fontcolor="black"] "redis_promote_0 redis-bundle-0" -> "redis-bundle-master_promoted_0" [ style = bold] "redis_promote_0 redis-bundle-0" -> "redis_monitor_20000 redis-bundle-0" [ style = bold] "redis_promote_0 redis-bundle-0" [ style=bold color="green" fontcolor="black"] } diff --git a/pengine/test10/bundle-order-partial-start-2.exp b/pengine/test10/bundle-order-partial-start-2.exp index 621332c1ab..08f508434d 100644 --- a/pengine/test10/bundle-order-partial-start-2.exp +++ b/pengine/test10/bundle-order-partial-start-2.exp @@ -1,299 +1,412 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + diff --git a/pengine/test10/bundle-order-partial-start-2.summary b/pengine/test10/bundle-order-partial-start-2.summary index 859ca25df3..5e3927cb6d 100644 --- a/pengine/test10/bundle-order-partial-start-2.summary +++ b/pengine/test10/bundle-order-partial-start-2.summary @@ -1,75 +1,84 @@ Current cluster status: Online: [ undercloud ] Containers: [ galera-bundle-0:galera-bundle-docker-0 rabbitmq-bundle-0:rabbitmq-bundle-docker-0 redis-bundle-0:redis-bundle-docker-0 ] Docker container: rabbitmq-bundle [192.168.24.1:8787/tripleoupstream/centos-binary-rabbitmq:latest] rabbitmq-bundle-0 (ocf::heartbeat:rabbitmq-cluster): Stopped undercloud Docker container: galera-bundle [192.168.24.1:8787/tripleoupstream/centos-binary-mariadb:latest] galera-bundle-0 (ocf::heartbeat:galera): Stopped undercloud Docker container: redis-bundle [192.168.24.1:8787/tripleoupstream/centos-binary-redis:latest] redis-bundle-0 (ocf::heartbeat:redis): Slave undercloud ip-192.168.122.254 (ocf::heartbeat:IPaddr2): Started undercloud ip-192.168.122.250 (ocf::heartbeat:IPaddr2): Started undercloud ip-192.168.122.249 (ocf::heartbeat:IPaddr2): Started undercloud ip-192.168.122.253 (ocf::heartbeat:IPaddr2): Started undercloud ip-192.168.122.247 (ocf::heartbeat:IPaddr2): Started undercloud ip-192.168.122.248 (ocf::heartbeat:IPaddr2): Started undercloud Docker container: haproxy-bundle [192.168.24.1:8787/tripleoupstream/centos-binary-haproxy:latest] haproxy-bundle-docker-0 (ocf::heartbeat:docker): Stopped Docker container: openstack-cinder-volume [192.168.24.1:8787/tripleoupstream/centos-binary-cinder-volume:latest] openstack-cinder-volume-docker-0 (ocf::heartbeat:docker): Started undercloud Transition Summary: * Start rabbitmq:0 (rabbitmq-bundle-0) + * Restart galera-bundle-docker-0 (Started undercloud) + * Restart galera-bundle-0 (Started undercloud) * Start galera:0 (galera-bundle-0) * Promote redis:0 (Slave -> Master redis-bundle-0) * Start haproxy-bundle-docker-0 (undercloud) Executing cluster transition: + * Resource action: galera-bundle-0 stop on undercloud * Resource action: haproxy-bundle-docker-0 monitor on undercloud * Pseudo action: haproxy-bundle_start_0 * Pseudo action: redis-bundle_promote_0 * Pseudo action: redis-bundle-master_promote_0 * Pseudo action: rabbitmq-bundle_start_0 + * Resource action: galera-bundle-docker-0 stop on undercloud * Resource action: redis promote on redis-bundle-0 * Resource action: haproxy-bundle-docker-0 start on undercloud * Pseudo action: haproxy-bundle_running_0 * Pseudo action: redis-bundle-master_promoted_0 * Pseudo action: rabbitmq-bundle-clone_start_0 + * Pseudo action: all_stopped * Resource action: rabbitmq:0 start on rabbitmq-bundle-0 * Resource action: redis monitor=20000 on redis-bundle-0 * Resource action: haproxy-bundle-docker-0 monitor=60000 on undercloud * Pseudo action: redis-bundle_promoted_0 * Pseudo action: rabbitmq-bundle-clone_running_0 * Pseudo action: rabbitmq-bundle_running_0 * Resource action: rabbitmq:0 monitor=10000 on rabbitmq-bundle-0 * Pseudo action: galera-bundle_start_0 + * Resource action: galera-bundle-docker-0 start on undercloud + * Resource action: galera-bundle-docker-0 monitor=60000 on undercloud + * Resource action: galera-bundle-0 start on undercloud + * Resource action: galera-bundle-0 monitor=60000 on undercloud * Pseudo action: galera-bundle-master_start_0 * Resource action: galera:0 start on galera-bundle-0 * Pseudo action: galera-bundle-master_running_0 * Pseudo action: galera-bundle_running_0 * Resource action: galera:0 monitor=30000 on galera-bundle-0 * Resource action: galera:0 monitor=20000 on galera-bundle-0 Revised cluster status: Online: [ undercloud ] Containers: [ galera-bundle-0:galera-bundle-docker-0 rabbitmq-bundle-0:rabbitmq-bundle-docker-0 redis-bundle-0:redis-bundle-docker-0 ] Docker container: rabbitmq-bundle [192.168.24.1:8787/tripleoupstream/centos-binary-rabbitmq:latest] rabbitmq-bundle-0 (ocf::heartbeat:rabbitmq-cluster): Started undercloud Docker container: galera-bundle [192.168.24.1:8787/tripleoupstream/centos-binary-mariadb:latest] galera-bundle-0 (ocf::heartbeat:galera): Slave undercloud Docker container: redis-bundle [192.168.24.1:8787/tripleoupstream/centos-binary-redis:latest] redis-bundle-0 (ocf::heartbeat:redis): Master undercloud ip-192.168.122.254 (ocf::heartbeat:IPaddr2): Started undercloud ip-192.168.122.250 (ocf::heartbeat:IPaddr2): Started undercloud ip-192.168.122.249 (ocf::heartbeat:IPaddr2): Started undercloud ip-192.168.122.253 (ocf::heartbeat:IPaddr2): Started undercloud ip-192.168.122.247 (ocf::heartbeat:IPaddr2): Started undercloud ip-192.168.122.248 (ocf::heartbeat:IPaddr2): Started undercloud Docker container: haproxy-bundle [192.168.24.1:8787/tripleoupstream/centos-binary-haproxy:latest] haproxy-bundle-docker-0 (ocf::heartbeat:docker): Started undercloud Docker container: openstack-cinder-volume [192.168.24.1:8787/tripleoupstream/centos-binary-cinder-volume:latest] openstack-cinder-volume-docker-0 (ocf::heartbeat:docker): Started undercloud diff --git a/pengine/test10/bundle-order-partial-start.dot b/pengine/test10/bundle-order-partial-start.dot index 06c3620d30..c36ff04cf2 100644 --- a/pengine/test10/bundle-order-partial-start.dot +++ b/pengine/test10/bundle-order-partial-start.dot @@ -1,65 +1,66 @@ digraph "g" { "galera-bundle-0_monitor_60000 undercloud" [ style=bold color="green" fontcolor="black"] "galera-bundle-0_start_0 undercloud" -> "galera-bundle-0_monitor_60000 undercloud" [ style = bold] "galera-bundle-0_start_0 undercloud" -> "galera:0_monitor_20000 galera-bundle-0" [ style = bold] "galera-bundle-0_start_0 undercloud" -> "galera:0_monitor_30000 galera-bundle-0" [ style = bold] "galera-bundle-0_start_0 undercloud" -> "galera:0_start_0 galera-bundle-0" [ style = bold] "galera-bundle-0_start_0 undercloud" [ style=bold color="green" fontcolor="black"] "galera-bundle-docker-0_monitor_0 undercloud" -> "galera-bundle-docker-0_start_0 undercloud" [ style = bold] "galera-bundle-docker-0_monitor_0 undercloud" [ style=bold color="green" fontcolor="black"] "galera-bundle-docker-0_monitor_60000 undercloud" [ style=bold color="green" fontcolor="black"] "galera-bundle-docker-0_start_0 undercloud" -> "galera-bundle-0_start_0 undercloud" [ style = bold] "galera-bundle-docker-0_start_0 undercloud" -> "galera-bundle-docker-0_monitor_60000 undercloud" [ style = bold] "galera-bundle-docker-0_start_0 undercloud" -> "galera:0_start_0 galera-bundle-0" [ style = bold] "galera-bundle-docker-0_start_0 undercloud" [ style=bold color="green" fontcolor="black"] "galera-bundle-master_running_0" -> "galera-bundle_running_0" [ style = bold] "galera-bundle-master_running_0" [ style=bold color="green" fontcolor="orange"] "galera-bundle-master_start_0" -> "galera-bundle-master_running_0" [ style = bold] "galera-bundle-master_start_0" -> "galera:0_start_0 galera-bundle-0" [ style = bold] "galera-bundle-master_start_0" [ style=bold color="green" fontcolor="orange"] "galera-bundle_running_0" [ style=bold color="green" fontcolor="orange"] "galera-bundle_start_0" -> "galera-bundle-docker-0_start_0 undercloud" [ style = bold] "galera-bundle_start_0" -> "galera-bundle-master_start_0" [ style = bold] "galera-bundle_start_0" [ style=bold color="green" fontcolor="orange"] "galera:0_monitor_20000 galera-bundle-0" [ style=bold color="green" fontcolor="black"] "galera:0_monitor_30000 galera-bundle-0" [ style=bold color="green" fontcolor="black"] "galera:0_start_0 galera-bundle-0" -> "galera-bundle-master_running_0" [ style = bold] "galera:0_start_0 galera-bundle-0" -> "galera:0_monitor_20000 galera-bundle-0" [ style = bold] "galera:0_start_0 galera-bundle-0" -> "galera:0_monitor_30000 galera-bundle-0" [ style = bold] "galera:0_start_0 galera-bundle-0" [ style=bold color="green" fontcolor="black"] "haproxy-bundle-docker-0_monitor_0 undercloud" -> "haproxy-bundle-docker-0_start_0 undercloud" [ style = bold] "haproxy-bundle-docker-0_monitor_0 undercloud" [ style=bold color="green" fontcolor="black"] "haproxy-bundle-docker-0_monitor_60000 undercloud" [ style=bold color="green" fontcolor="black"] +"haproxy-bundle-docker-0_start_0 undercloud" -> "galera-bundle-docker-0_start_0 undercloud" [ style = bold] "haproxy-bundle-docker-0_start_0 undercloud" -> "haproxy-bundle-docker-0_monitor_60000 undercloud" [ style = bold] "haproxy-bundle-docker-0_start_0 undercloud" -> "haproxy-bundle_running_0" [ style = bold] "haproxy-bundle-docker-0_start_0 undercloud" [ style=bold color="green" fontcolor="black"] "haproxy-bundle_running_0" -> "galera-bundle_start_0" [ style = bold] "haproxy-bundle_running_0" [ style=bold color="green" fontcolor="orange"] "haproxy-bundle_start_0" -> "haproxy-bundle-docker-0_start_0 undercloud" [ style = bold] "haproxy-bundle_start_0" [ style=bold color="green" fontcolor="orange"] "rabbitmq-bundle-clone_running_0" -> "rabbitmq-bundle_running_0" [ style = bold] "rabbitmq-bundle-clone_running_0" [ style=bold color="green" fontcolor="orange"] "rabbitmq-bundle-clone_start_0" -> "rabbitmq-bundle-clone_running_0" [ style = bold] "rabbitmq-bundle-clone_start_0" -> "rabbitmq:0_start_0 rabbitmq-bundle-0" [ style = bold] "rabbitmq-bundle-clone_start_0" [ style=bold color="green" fontcolor="orange"] "rabbitmq-bundle_running_0" -> "galera-bundle_start_0" [ style = bold] "rabbitmq-bundle_running_0" [ style=bold color="green" fontcolor="orange"] "rabbitmq-bundle_start_0" -> "rabbitmq-bundle-clone_start_0" [ style = bold] "rabbitmq-bundle_start_0" [ style=bold color="green" fontcolor="orange"] "rabbitmq:0_monitor_10000 rabbitmq-bundle-0" [ style=bold color="green" fontcolor="black"] "rabbitmq:0_start_0 rabbitmq-bundle-0" -> "rabbitmq-bundle-clone_running_0" [ style = bold] "rabbitmq:0_start_0 rabbitmq-bundle-0" -> "rabbitmq:0_monitor_10000 rabbitmq-bundle-0" [ style = bold] "rabbitmq:0_start_0 rabbitmq-bundle-0" [ style=bold color="green" fontcolor="black"] "redis-bundle-master_promote_0" -> "redis_promote_0 redis-bundle-0" [ style = bold] "redis-bundle-master_promote_0" [ style=bold color="green" fontcolor="orange"] "redis-bundle-master_promoted_0" -> "redis-bundle_promoted_0" [ style = bold] "redis-bundle-master_promoted_0" [ style=bold color="green" fontcolor="orange"] "redis-bundle_promote_0" -> "redis-bundle-master_promote_0" [ style = bold] "redis-bundle_promote_0" [ style=bold color="green" fontcolor="orange"] "redis-bundle_promoted_0" -> "galera-bundle_start_0" [ style = bold] "redis-bundle_promoted_0" [ style=bold color="green" fontcolor="orange"] "redis_monitor_20000 redis-bundle-0" [ style=bold color="green" fontcolor="black"] "redis_promote_0 redis-bundle-0" -> "redis-bundle-master_promoted_0" [ style = bold] "redis_promote_0 redis-bundle-0" -> "redis_monitor_20000 redis-bundle-0" [ style = bold] "redis_promote_0 redis-bundle-0" [ style=bold color="green" fontcolor="black"] } diff --git a/pengine/test10/bundle-order-partial-start.exp b/pengine/test10/bundle-order-partial-start.exp index b48bb033c8..4a5c01cd36 100644 --- a/pengine/test10/bundle-order-partial-start.exp +++ b/pengine/test10/bundle-order-partial-start.exp @@ -1,375 +1,378 @@ + + + diff --git a/pengine/test10/bundle-order-partial-stop.dot b/pengine/test10/bundle-order-partial-stop.dot index 235b634585..af2493c251 100644 --- a/pengine/test10/bundle-order-partial-stop.dot +++ b/pengine/test10/bundle-order-partial-stop.dot @@ -1,194 +1,207 @@ digraph "g" { "Cancel galera_monitor_10000 galera-bundle-0" -> "galera_demote_0 galera-bundle-0" [ style = bold] "Cancel galera_monitor_10000 galera-bundle-0" [ style=bold color="green" fontcolor="black"] "Cancel redis_monitor_20000 redis-bundle-0" -> "redis_demote_0 redis-bundle-0" [ style = bold] "Cancel redis_monitor_20000 redis-bundle-0" [ style=bold color="green" fontcolor="black"] "all_stopped" [ style=bold color="green" fontcolor="orange"] "do_shutdown undercloud" [ style=bold color="green" fontcolor="black"] "galera-bundle-0_stop_0 undercloud" -> "all_stopped" [ style = bold] "galera-bundle-0_stop_0 undercloud" -> "do_shutdown undercloud" [ style = bold] "galera-bundle-0_stop_0 undercloud" -> "galera-bundle-docker-0_stop_0 undercloud" [ style = bold] "galera-bundle-0_stop_0 undercloud" [ style=bold color="green" fontcolor="black"] "galera-bundle-docker-0_stop_0 undercloud" -> "all_stopped" [ style = bold] "galera-bundle-docker-0_stop_0 undercloud" -> "do_shutdown undercloud" [ style = bold] +"galera-bundle-docker-0_stop_0 undercloud" -> "redis-bundle-docker-0_stop_0 undercloud" [ style = bold] "galera-bundle-docker-0_stop_0 undercloud" [ style=bold color="green" fontcolor="black"] "galera-bundle-master_demote_0" -> "galera-bundle-master_demoted_0" [ style = bold] "galera-bundle-master_demote_0" -> "galera_demote_0 galera-bundle-0" [ style = bold] "galera-bundle-master_demote_0" [ style=bold color="green" fontcolor="orange"] "galera-bundle-master_demoted_0" -> "galera-bundle-master_start_0" [ style = dashed] "galera-bundle-master_demoted_0" -> "galera-bundle-master_stop_0" [ style = bold] "galera-bundle-master_demoted_0" -> "galera-bundle_demoted_0" [ style = bold] "galera-bundle-master_demoted_0" [ style=bold color="green" fontcolor="orange"] +"galera-bundle-master_running_0" -> "galera-bundle_running_0" [ style = dashed] "galera-bundle-master_running_0" [ style=dashed color="red" fontcolor="orange"] "galera-bundle-master_start_0" -> "galera-bundle-master_running_0" [ style = dashed] "galera-bundle-master_start_0" -> "galera_start_0 galera-bundle-0" [ style = dashed] "galera-bundle-master_start_0" [ style=dashed color="red" fontcolor="orange"] "galera-bundle-master_stop_0" -> "galera-bundle-master_stopped_0" [ style = bold] "galera-bundle-master_stop_0" -> "galera_stop_0 galera-bundle-0" [ style = bold] "galera-bundle-master_stop_0" [ style=bold color="green" fontcolor="orange"] "galera-bundle-master_stopped_0" -> "galera-bundle-master_start_0" [ style = dashed] "galera-bundle-master_stopped_0" -> "galera-bundle_stopped_0" [ style = bold] "galera-bundle-master_stopped_0" [ style=bold color="green" fontcolor="orange"] "galera-bundle_demote_0" -> "galera-bundle-master_demote_0" [ style = bold] "galera-bundle_demote_0" -> "galera-bundle_demoted_0" [ style = bold] "galera-bundle_demote_0" [ style=bold color="green" fontcolor="orange"] +"galera-bundle_demoted_0" -> "galera-bundle_start_0" [ style = dashed] "galera-bundle_demoted_0" -> "galera-bundle_stop_0" [ style = bold] "galera-bundle_demoted_0" [ style=bold color="green" fontcolor="orange"] +"galera-bundle_running_0" [ style=dashed color="red" fontcolor="orange"] +"galera-bundle_start_0" -> "galera-bundle-master_start_0" [ style = dashed] +"galera-bundle_start_0" [ style=dashed color="red" fontcolor="orange"] "galera-bundle_stop_0" -> "galera-bundle-master_stop_0" [ style = bold] "galera-bundle_stop_0" -> "galera_stop_0 galera-bundle-0" [ style = bold] "galera-bundle_stop_0" [ style=bold color="green" fontcolor="orange"] +"galera-bundle_stopped_0" -> "galera-bundle_start_0" [ style = dashed] "galera-bundle_stopped_0" -> "redis-bundle_stop_0" [ style = bold] "galera-bundle_stopped_0" [ style=bold color="green" fontcolor="orange"] "galera_demote_0 galera-bundle-0" -> "galera-bundle-0_stop_0 undercloud" [ style = bold] "galera_demote_0 galera-bundle-0" -> "galera-bundle-master_demoted_0" [ style = bold] "galera_demote_0 galera-bundle-0" -> "galera_monitor_20000 galera-bundle-0" [ style = dashed] "galera_demote_0 galera-bundle-0" -> "galera_monitor_30000 galera-bundle-0" [ style = dashed] "galera_demote_0 galera-bundle-0" -> "galera_stop_0 galera-bundle-0" [ style = bold] "galera_demote_0 galera-bundle-0" [ style=bold color="green" fontcolor="black"] "galera_monitor_20000 galera-bundle-0" [ style=dashed color="red" fontcolor="black"] "galera_monitor_30000 galera-bundle-0" [ style=dashed color="red" fontcolor="black"] "galera_start_0 galera-bundle-0" -> "galera-bundle-master_running_0" [ style = dashed] "galera_start_0 galera-bundle-0" -> "galera_monitor_20000 galera-bundle-0" [ style = dashed] "galera_start_0 galera-bundle-0" -> "galera_monitor_30000 galera-bundle-0" [ style = dashed] "galera_start_0 galera-bundle-0" [ style=dashed color="red" fontcolor="black"] "galera_stop_0 galera-bundle-0" -> "all_stopped" [ style = bold] "galera_stop_0 galera-bundle-0" -> "galera-bundle-0_stop_0 undercloud" [ style = bold] "galera_stop_0 galera-bundle-0" -> "galera-bundle-master_stopped_0" [ style = bold] "galera_stop_0 galera-bundle-0" -> "galera_start_0 galera-bundle-0" [ style = dashed] "galera_stop_0 galera-bundle-0" [ style=bold color="green" fontcolor="black"] "haproxy-bundle-docker-0_stop_0 undercloud" -> "all_stopped" [ style = bold] "haproxy-bundle-docker-0_stop_0 undercloud" -> "do_shutdown undercloud" [ style = bold] "haproxy-bundle-docker-0_stop_0 undercloud" -> "haproxy-bundle_stopped_0" [ style = bold] "haproxy-bundle-docker-0_stop_0 undercloud" [ style=bold color="green" fontcolor="black"] "haproxy-bundle_stop_0" -> "haproxy-bundle-docker-0_stop_0 undercloud" [ style = bold] "haproxy-bundle_stop_0" [ style=bold color="green" fontcolor="orange"] "haproxy-bundle_stopped_0" -> "ip-192.168.122.247_stop_0 undercloud" [ style = bold] "haproxy-bundle_stopped_0" -> "ip-192.168.122.248_stop_0 undercloud" [ style = bold] "haproxy-bundle_stopped_0" -> "ip-192.168.122.249_stop_0 undercloud" [ style = bold] "haproxy-bundle_stopped_0" -> "ip-192.168.122.250_stop_0 undercloud" [ style = bold] "haproxy-bundle_stopped_0" -> "ip-192.168.122.253_stop_0 undercloud" [ style = bold] "haproxy-bundle_stopped_0" -> "ip-192.168.122.254_stop_0 undercloud" [ style = bold] "haproxy-bundle_stopped_0" [ style=bold color="green" fontcolor="orange"] "ip-192.168.122.247_start_0 " [ style=dashed color="red" fontcolor="black"] "ip-192.168.122.247_stop_0 undercloud" -> "all_stopped" [ style = bold] "ip-192.168.122.247_stop_0 undercloud" -> "do_shutdown undercloud" [ style = bold] "ip-192.168.122.247_stop_0 undercloud" -> "ip-192.168.122.247_start_0 " [ style = dashed] "ip-192.168.122.247_stop_0 undercloud" [ style=bold color="green" fontcolor="black"] "ip-192.168.122.248_start_0 " [ style=dashed color="red" fontcolor="black"] "ip-192.168.122.248_stop_0 undercloud" -> "all_stopped" [ style = bold] "ip-192.168.122.248_stop_0 undercloud" -> "do_shutdown undercloud" [ style = bold] "ip-192.168.122.248_stop_0 undercloud" -> "ip-192.168.122.248_start_0 " [ style = dashed] "ip-192.168.122.248_stop_0 undercloud" [ style=bold color="green" fontcolor="black"] "ip-192.168.122.249_start_0 " [ style=dashed color="red" fontcolor="black"] "ip-192.168.122.249_stop_0 undercloud" -> "all_stopped" [ style = bold] "ip-192.168.122.249_stop_0 undercloud" -> "do_shutdown undercloud" [ style = bold] "ip-192.168.122.249_stop_0 undercloud" -> "ip-192.168.122.249_start_0 " [ style = dashed] "ip-192.168.122.249_stop_0 undercloud" [ style=bold color="green" fontcolor="black"] "ip-192.168.122.250_start_0 " [ style=dashed color="red" fontcolor="black"] "ip-192.168.122.250_stop_0 undercloud" -> "all_stopped" [ style = bold] "ip-192.168.122.250_stop_0 undercloud" -> "do_shutdown undercloud" [ style = bold] "ip-192.168.122.250_stop_0 undercloud" -> "ip-192.168.122.250_start_0 " [ style = dashed] "ip-192.168.122.250_stop_0 undercloud" [ style=bold color="green" fontcolor="black"] "ip-192.168.122.253_start_0 " [ style=dashed color="red" fontcolor="black"] "ip-192.168.122.253_stop_0 undercloud" -> "all_stopped" [ style = bold] "ip-192.168.122.253_stop_0 undercloud" -> "do_shutdown undercloud" [ style = bold] "ip-192.168.122.253_stop_0 undercloud" -> "ip-192.168.122.253_start_0 " [ style = dashed] "ip-192.168.122.253_stop_0 undercloud" [ style=bold color="green" fontcolor="black"] "ip-192.168.122.254_start_0 " [ style=dashed color="red" fontcolor="black"] "ip-192.168.122.254_stop_0 undercloud" -> "all_stopped" [ style = bold] "ip-192.168.122.254_stop_0 undercloud" -> "do_shutdown undercloud" [ style = bold] "ip-192.168.122.254_stop_0 undercloud" -> "ip-192.168.122.254_start_0 " [ style = dashed] "ip-192.168.122.254_stop_0 undercloud" [ style=bold color="green" fontcolor="black"] "openstack-cinder-volume-docker-0_stop_0 undercloud" -> "all_stopped" [ style = bold] "openstack-cinder-volume-docker-0_stop_0 undercloud" -> "do_shutdown undercloud" [ style = bold] "openstack-cinder-volume-docker-0_stop_0 undercloud" -> "openstack-cinder-volume_stopped_0" [ style = bold] "openstack-cinder-volume-docker-0_stop_0 undercloud" [ style=bold color="green" fontcolor="black"] "openstack-cinder-volume_stop_0" -> "openstack-cinder-volume-docker-0_stop_0 undercloud" [ style = bold] "openstack-cinder-volume_stop_0" [ style=bold color="green" fontcolor="orange"] "openstack-cinder-volume_stopped_0" [ style=bold color="green" fontcolor="orange"] "rabbitmq-bundle-0_stop_0 undercloud" -> "all_stopped" [ style = bold] "rabbitmq-bundle-0_stop_0 undercloud" -> "do_shutdown undercloud" [ style = bold] "rabbitmq-bundle-0_stop_0 undercloud" -> "rabbitmq-bundle-docker-0_stop_0 undercloud" [ style = bold] "rabbitmq-bundle-0_stop_0 undercloud" [ style=bold color="green" fontcolor="black"] +"rabbitmq-bundle-clone_running_0" -> "rabbitmq-bundle_running_0" [ style = dashed] "rabbitmq-bundle-clone_running_0" [ style=dashed color="red" fontcolor="orange"] "rabbitmq-bundle-clone_start_0" -> "rabbitmq-bundle-clone_running_0" [ style = dashed] "rabbitmq-bundle-clone_start_0" -> "rabbitmq_start_0 rabbitmq-bundle-0" [ style = dashed] "rabbitmq-bundle-clone_start_0" [ style=dashed color="red" fontcolor="orange"] "rabbitmq-bundle-clone_stop_0" -> "rabbitmq-bundle-clone_stopped_0" [ style = bold] "rabbitmq-bundle-clone_stop_0" -> "rabbitmq_stop_0 rabbitmq-bundle-0" [ style = bold] "rabbitmq-bundle-clone_stop_0" [ style=bold color="green" fontcolor="orange"] "rabbitmq-bundle-clone_stopped_0" -> "rabbitmq-bundle-clone_start_0" [ style = dashed] "rabbitmq-bundle-clone_stopped_0" -> "rabbitmq-bundle_stopped_0" [ style = bold] "rabbitmq-bundle-clone_stopped_0" [ style=bold color="green" fontcolor="orange"] "rabbitmq-bundle-docker-0_stop_0 undercloud" -> "all_stopped" [ style = bold] "rabbitmq-bundle-docker-0_stop_0 undercloud" -> "do_shutdown undercloud" [ style = bold] "rabbitmq-bundle-docker-0_stop_0 undercloud" [ style=bold color="green" fontcolor="black"] +"rabbitmq-bundle_running_0" [ style=dashed color="red" fontcolor="orange"] "rabbitmq-bundle_stop_0" -> "rabbitmq-bundle-clone_stop_0" [ style = bold] "rabbitmq-bundle_stop_0" -> "rabbitmq_stop_0 rabbitmq-bundle-0" [ style = bold] "rabbitmq-bundle_stop_0" [ style=bold color="green" fontcolor="orange"] "rabbitmq-bundle_stopped_0" [ style=bold color="green" fontcolor="orange"] "rabbitmq_monitor_10000 rabbitmq-bundle-0" [ style=dashed color="red" fontcolor="black"] "rabbitmq_start_0 rabbitmq-bundle-0" -> "rabbitmq-bundle-clone_running_0" [ style = dashed] "rabbitmq_start_0 rabbitmq-bundle-0" -> "rabbitmq_monitor_10000 rabbitmq-bundle-0" [ style = dashed] "rabbitmq_start_0 rabbitmq-bundle-0" [ style=dashed color="red" fontcolor="black"] "rabbitmq_stop_0 rabbitmq-bundle-0" -> "all_stopped" [ style = bold] "rabbitmq_stop_0 rabbitmq-bundle-0" -> "rabbitmq-bundle-0_stop_0 undercloud" [ style = bold] "rabbitmq_stop_0 rabbitmq-bundle-0" -> "rabbitmq-bundle-clone_stopped_0" [ style = bold] "rabbitmq_stop_0 rabbitmq-bundle-0" -> "rabbitmq_start_0 rabbitmq-bundle-0" [ style = dashed] "rabbitmq_stop_0 rabbitmq-bundle-0" [ style=bold color="green" fontcolor="black"] "redis-bundle-0_stop_0 undercloud" -> "all_stopped" [ style = bold] "redis-bundle-0_stop_0 undercloud" -> "do_shutdown undercloud" [ style = bold] "redis-bundle-0_stop_0 undercloud" -> "redis-bundle-docker-0_stop_0 undercloud" [ style = bold] "redis-bundle-0_stop_0 undercloud" [ style=bold color="green" fontcolor="black"] "redis-bundle-docker-0_stop_0 undercloud" -> "all_stopped" [ style = bold] "redis-bundle-docker-0_stop_0 undercloud" -> "do_shutdown undercloud" [ style = bold] +"redis-bundle-docker-0_stop_0 undercloud" -> "haproxy-bundle-docker-0_stop_0 undercloud" [ style = bold] "redis-bundle-docker-0_stop_0 undercloud" [ style=bold color="green" fontcolor="black"] "redis-bundle-master_demote_0" -> "redis-bundle-master_demoted_0" [ style = bold] "redis-bundle-master_demote_0" -> "redis_demote_0 redis-bundle-0" [ style = bold] "redis-bundle-master_demote_0" [ style=bold color="green" fontcolor="orange"] "redis-bundle-master_demoted_0" -> "redis-bundle-master_start_0" [ style = dashed] "redis-bundle-master_demoted_0" -> "redis-bundle-master_stop_0" [ style = bold] "redis-bundle-master_demoted_0" -> "redis-bundle_demoted_0" [ style = bold] "redis-bundle-master_demoted_0" [ style=bold color="green" fontcolor="orange"] +"redis-bundle-master_running_0" -> "redis-bundle_running_0" [ style = dashed] "redis-bundle-master_running_0" [ style=dashed color="red" fontcolor="orange"] "redis-bundle-master_start_0" -> "redis-bundle-master_running_0" [ style = dashed] "redis-bundle-master_start_0" -> "redis_start_0 redis-bundle-0" [ style = dashed] "redis-bundle-master_start_0" [ style=dashed color="red" fontcolor="orange"] "redis-bundle-master_stop_0" -> "redis-bundle-master_stopped_0" [ style = bold] "redis-bundle-master_stop_0" -> "redis_stop_0 redis-bundle-0" [ style = bold] "redis-bundle-master_stop_0" [ style=bold color="green" fontcolor="orange"] "redis-bundle-master_stopped_0" -> "redis-bundle-master_start_0" [ style = dashed] "redis-bundle-master_stopped_0" -> "redis-bundle_stopped_0" [ style = bold] "redis-bundle-master_stopped_0" [ style=bold color="green" fontcolor="orange"] "redis-bundle_demote_0" -> "redis-bundle-master_demote_0" [ style = bold] "redis-bundle_demote_0" -> "redis-bundle_demoted_0" [ style = bold] "redis-bundle_demote_0" [ style=bold color="green" fontcolor="orange"] -"redis-bundle_demoted_0" -> "redis-bundle_start_0" [ style = bold] +"redis-bundle_demoted_0" -> "redis-bundle_start_0" [ style = dashed] "redis-bundle_demoted_0" -> "redis-bundle_stop_0" [ style = bold] "redis-bundle_demoted_0" [ style=bold color="green" fontcolor="orange"] +"redis-bundle_running_0" -> "galera-bundle_start_0" [ style = dashed] +"redis-bundle_running_0" [ style=dashed color="red" fontcolor="orange"] "redis-bundle_start_0" -> "redis-bundle-master_start_0" [ style = dashed] -"redis-bundle_start_0" [ style=bold color="green" fontcolor="orange"] +"redis-bundle_start_0" [ style=dashed color="red" fontcolor="orange"] "redis-bundle_stop_0" -> "redis-bundle-master_stop_0" [ style = bold] "redis-bundle_stop_0" -> "redis_stop_0 redis-bundle-0" [ style = bold] "redis-bundle_stop_0" [ style=bold color="green" fontcolor="orange"] "redis-bundle_stopped_0" -> "haproxy-bundle_stop_0" [ style = bold] -"redis-bundle_stopped_0" -> "redis-bundle_start_0" [ style = bold] +"redis-bundle_stopped_0" -> "redis-bundle_start_0" [ style = dashed] "redis-bundle_stopped_0" [ style=bold color="green" fontcolor="orange"] "redis_demote_0 redis-bundle-0" -> "redis-bundle-0_stop_0 undercloud" [ style = bold] "redis_demote_0 redis-bundle-0" -> "redis-bundle-master_demoted_0" [ style = bold] "redis_demote_0 redis-bundle-0" -> "redis_monitor_45000 redis-bundle-0" [ style = dashed] "redis_demote_0 redis-bundle-0" -> "redis_monitor_60000 redis-bundle-0" [ style = dashed] "redis_demote_0 redis-bundle-0" -> "redis_stop_0 redis-bundle-0" [ style = bold] "redis_demote_0 redis-bundle-0" [ style=bold color="green" fontcolor="black"] "redis_monitor_45000 redis-bundle-0" [ style=dashed color="red" fontcolor="black"] "redis_monitor_60000 redis-bundle-0" [ style=dashed color="red" fontcolor="black"] "redis_start_0 redis-bundle-0" -> "redis-bundle-master_running_0" [ style = dashed] "redis_start_0 redis-bundle-0" -> "redis_monitor_45000 redis-bundle-0" [ style = dashed] "redis_start_0 redis-bundle-0" -> "redis_monitor_60000 redis-bundle-0" [ style = dashed] "redis_start_0 redis-bundle-0" [ style=dashed color="red" fontcolor="black"] "redis_stop_0 redis-bundle-0" -> "all_stopped" [ style = bold] "redis_stop_0 redis-bundle-0" -> "redis-bundle-0_stop_0 undercloud" [ style = bold] "redis_stop_0 redis-bundle-0" -> "redis-bundle-master_stopped_0" [ style = bold] "redis_stop_0 redis-bundle-0" -> "redis_start_0 redis-bundle-0" [ style = dashed] "redis_stop_0 redis-bundle-0" [ style=bold color="green" fontcolor="black"] } diff --git a/pengine/test10/bundle-order-partial-stop.exp b/pengine/test10/bundle-order-partial-stop.exp index d0854331d8..02a73722cf 100644 --- a/pengine/test10/bundle-order-partial-stop.exp +++ b/pengine/test10/bundle-order-partial-stop.exp @@ -1,734 +1,725 @@ + + + + + + - - - - - - - - - - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/pengine/test10/bundle-order-partial-stop.summary b/pengine/test10/bundle-order-partial-stop.summary index bd6f937515..c341f4c635 100644 --- a/pengine/test10/bundle-order-partial-stop.summary +++ b/pengine/test10/bundle-order-partial-stop.summary @@ -1,114 +1,113 @@ Current cluster status: Online: [ undercloud ] Containers: [ galera-bundle-0:galera-bundle-docker-0 rabbitmq-bundle-0:rabbitmq-bundle-docker-0 redis-bundle-0:redis-bundle-docker-0 ] Docker container: rabbitmq-bundle [192.168.24.1:8787/tripleoupstream/centos-binary-rabbitmq:latest] rabbitmq-bundle-0 (ocf::heartbeat:rabbitmq-cluster): Started undercloud Docker container: galera-bundle [192.168.24.1:8787/tripleoupstream/centos-binary-mariadb:latest] galera-bundle-0 (ocf::heartbeat:galera): Master undercloud Docker container: redis-bundle [192.168.24.1:8787/tripleoupstream/centos-binary-redis:latest] redis-bundle-0 (ocf::heartbeat:redis): Master undercloud ip-192.168.122.254 (ocf::heartbeat:IPaddr2): Started undercloud ip-192.168.122.250 (ocf::heartbeat:IPaddr2): Started undercloud ip-192.168.122.249 (ocf::heartbeat:IPaddr2): Started undercloud ip-192.168.122.253 (ocf::heartbeat:IPaddr2): Started undercloud ip-192.168.122.247 (ocf::heartbeat:IPaddr2): Started undercloud ip-192.168.122.248 (ocf::heartbeat:IPaddr2): Started undercloud Docker container: haproxy-bundle [192.168.24.1:8787/tripleoupstream/centos-binary-haproxy:latest] haproxy-bundle-docker-0 (ocf::heartbeat:docker): Started undercloud Docker container: openstack-cinder-volume [192.168.24.1:8787/tripleoupstream/centos-binary-cinder-volume:latest] openstack-cinder-volume-docker-0 (ocf::heartbeat:docker): Started undercloud Transition Summary: * Shutdown undercloud * Stop rabbitmq-bundle-docker-0 (undercloud) * Stop rabbitmq-bundle-0 (undercloud) * Stop rabbitmq:0 (Started rabbitmq-bundle-0) * Stop galera-bundle-docker-0 (undercloud) * Stop galera-bundle-0 (undercloud) * Demote galera:0 (Master -> Slave galera-bundle-0) * Restart galera:0 (Slave galera-bundle-0) * Stop redis-bundle-docker-0 (undercloud) * Stop redis-bundle-0 (undercloud) * Demote redis:0 (Master -> Slave redis-bundle-0) * Restart redis:0 (Slave redis-bundle-0) * Stop ip-192.168.122.254 (undercloud) * Stop ip-192.168.122.250 (undercloud) * Stop ip-192.168.122.249 (undercloud) * Stop ip-192.168.122.253 (undercloud) * Stop ip-192.168.122.247 (undercloud) * Stop ip-192.168.122.248 (undercloud) * Stop haproxy-bundle-docker-0 (undercloud) * Stop openstack-cinder-volume-docker-0 (undercloud) Executing cluster transition: * Resource action: galera cancel=10000 on galera-bundle-0 * Resource action: redis cancel=20000 on redis-bundle-0 * Pseudo action: openstack-cinder-volume_stop_0 * Pseudo action: redis-bundle_demote_0 * Pseudo action: redis-bundle-master_demote_0 * Pseudo action: galera-bundle_demote_0 * Pseudo action: galera-bundle-master_demote_0 * Pseudo action: rabbitmq-bundle_stop_0 * Resource action: galera demote on galera-bundle-0 * Resource action: redis demote on redis-bundle-0 * Resource action: openstack-cinder-volume-docker-0 stop on undercloud * Pseudo action: openstack-cinder-volume_stopped_0 * Pseudo action: redis-bundle-master_demoted_0 * Pseudo action: galera-bundle-master_demoted_0 * Pseudo action: rabbitmq-bundle-clone_stop_0 * Resource action: rabbitmq stop on rabbitmq-bundle-0 * Resource action: rabbitmq-bundle-0 stop on undercloud * Pseudo action: redis-bundle_demoted_0 * Pseudo action: galera-bundle_demoted_0 * Pseudo action: galera-bundle_stop_0 * Pseudo action: rabbitmq-bundle-clone_stopped_0 * Pseudo action: rabbitmq-bundle_stopped_0 * Resource action: rabbitmq-bundle-docker-0 stop on undercloud * Pseudo action: galera-bundle-master_stop_0 * Resource action: galera stop on galera-bundle-0 * Resource action: galera-bundle-0 stop on undercloud * Pseudo action: galera-bundle-master_stopped_0 * Pseudo action: galera-bundle_stopped_0 * Resource action: galera-bundle-docker-0 stop on undercloud * Pseudo action: redis-bundle_stop_0 * Pseudo action: redis-bundle-master_stop_0 * Resource action: redis stop on redis-bundle-0 * Resource action: redis-bundle-0 stop on undercloud * Pseudo action: redis-bundle-master_stopped_0 * Pseudo action: redis-bundle_stopped_0 - * Pseudo action: redis-bundle_start_0 * Resource action: redis-bundle-docker-0 stop on undercloud * Pseudo action: haproxy-bundle_stop_0 * Resource action: haproxy-bundle-docker-0 stop on undercloud * Pseudo action: haproxy-bundle_stopped_0 * Resource action: ip-192.168.122.254 stop on undercloud * Resource action: ip-192.168.122.250 stop on undercloud * Resource action: ip-192.168.122.249 stop on undercloud * Resource action: ip-192.168.122.253 stop on undercloud * Resource action: ip-192.168.122.247 stop on undercloud * Resource action: ip-192.168.122.248 stop on undercloud * Cluster action: do_shutdown on undercloud * Pseudo action: all_stopped Revised cluster status: Online: [ undercloud ] Docker container: rabbitmq-bundle [192.168.24.1:8787/tripleoupstream/centos-binary-rabbitmq:latest] rabbitmq-bundle-0 (ocf::heartbeat:rabbitmq-cluster): Stopped Docker container: galera-bundle [192.168.24.1:8787/tripleoupstream/centos-binary-mariadb:latest] galera-bundle-0 (ocf::heartbeat:galera): Stopped Docker container: redis-bundle [192.168.24.1:8787/tripleoupstream/centos-binary-redis:latest] redis-bundle-0 (ocf::heartbeat:redis): Stopped ip-192.168.122.254 (ocf::heartbeat:IPaddr2): Stopped ip-192.168.122.250 (ocf::heartbeat:IPaddr2): Stopped ip-192.168.122.249 (ocf::heartbeat:IPaddr2): Stopped ip-192.168.122.253 (ocf::heartbeat:IPaddr2): Stopped ip-192.168.122.247 (ocf::heartbeat:IPaddr2): Stopped ip-192.168.122.248 (ocf::heartbeat:IPaddr2): Stopped Docker container: haproxy-bundle [192.168.24.1:8787/tripleoupstream/centos-binary-haproxy:latest] haproxy-bundle-docker-0 (ocf::heartbeat:docker): Stopped Docker container: openstack-cinder-volume [192.168.24.1:8787/tripleoupstream/centos-binary-cinder-volume:latest] openstack-cinder-volume-docker-0 (ocf::heartbeat:docker): Stopped diff --git a/pengine/test10/bundle-order-startup.dot b/pengine/test10/bundle-order-startup.dot index 73947a569f..4e60d7951c 100644 --- a/pengine/test10/bundle-order-startup.dot +++ b/pengine/test10/bundle-order-startup.dot @@ -1,139 +1,141 @@ digraph "g" { "galera-bundle-0_monitor_60000 undercloud" [ style=bold color="green" fontcolor="black"] "galera-bundle-0_start_0 undercloud" -> "galera-bundle-0_monitor_60000 undercloud" [ style = bold] "galera-bundle-0_start_0 undercloud" -> "galera:0_monitor_20000 galera-bundle-0" [ style = bold] "galera-bundle-0_start_0 undercloud" -> "galera:0_monitor_30000 galera-bundle-0" [ style = bold] "galera-bundle-0_start_0 undercloud" -> "galera:0_start_0 galera-bundle-0" [ style = bold] "galera-bundle-0_start_0 undercloud" [ style=bold color="green" fontcolor="black"] "galera-bundle-docker-0_monitor_0 undercloud" -> "galera-bundle-docker-0_start_0 undercloud" [ style = bold] "galera-bundle-docker-0_monitor_0 undercloud" [ style=bold color="green" fontcolor="black"] "galera-bundle-docker-0_monitor_60000 undercloud" [ style=bold color="green" fontcolor="black"] "galera-bundle-docker-0_start_0 undercloud" -> "galera-bundle-0_start_0 undercloud" [ style = bold] "galera-bundle-docker-0_start_0 undercloud" -> "galera-bundle-docker-0_monitor_60000 undercloud" [ style = bold] "galera-bundle-docker-0_start_0 undercloud" -> "galera:0_start_0 galera-bundle-0" [ style = bold] "galera-bundle-docker-0_start_0 undercloud" [ style=bold color="green" fontcolor="black"] "galera-bundle-master_running_0" -> "galera-bundle_running_0" [ style = bold] "galera-bundle-master_running_0" [ style=bold color="green" fontcolor="orange"] "galera-bundle-master_start_0" -> "galera-bundle-master_running_0" [ style = bold] "galera-bundle-master_start_0" -> "galera:0_start_0 galera-bundle-0" [ style = bold] "galera-bundle-master_start_0" [ style=bold color="green" fontcolor="orange"] "galera-bundle_running_0" [ style=bold color="green" fontcolor="orange"] "galera-bundle_start_0" -> "galera-bundle-docker-0_start_0 undercloud" [ style = bold] "galera-bundle_start_0" -> "galera-bundle-master_start_0" [ style = bold] "galera-bundle_start_0" [ style=bold color="green" fontcolor="orange"] "galera:0_monitor_20000 galera-bundle-0" [ style=bold color="green" fontcolor="black"] "galera:0_monitor_30000 galera-bundle-0" [ style=bold color="green" fontcolor="black"] "galera:0_start_0 galera-bundle-0" -> "galera-bundle-master_running_0" [ style = bold] "galera:0_start_0 galera-bundle-0" -> "galera:0_monitor_20000 galera-bundle-0" [ style = bold] "galera:0_start_0 galera-bundle-0" -> "galera:0_monitor_30000 galera-bundle-0" [ style = bold] "galera:0_start_0 galera-bundle-0" [ style=bold color="green" fontcolor="black"] "haproxy-bundle-docker-0_monitor_0 undercloud" -> "haproxy-bundle-docker-0_start_0 undercloud" [ style = bold] "haproxy-bundle-docker-0_monitor_0 undercloud" [ style=bold color="green" fontcolor="black"] "haproxy-bundle-docker-0_monitor_60000 undercloud" [ style=bold color="green" fontcolor="black"] "haproxy-bundle-docker-0_start_0 undercloud" -> "haproxy-bundle-docker-0_monitor_60000 undercloud" [ style = bold] "haproxy-bundle-docker-0_start_0 undercloud" -> "haproxy-bundle_running_0" [ style = bold] +"haproxy-bundle-docker-0_start_0 undercloud" -> "redis-bundle-docker-0_start_0 undercloud" [ style = bold] "haproxy-bundle-docker-0_start_0 undercloud" [ style=bold color="green" fontcolor="black"] "haproxy-bundle_running_0" -> "redis-bundle_start_0" [ style = bold] "haproxy-bundle_running_0" [ style=bold color="green" fontcolor="orange"] "haproxy-bundle_start_0" -> "haproxy-bundle-docker-0_start_0 undercloud" [ style = bold] "haproxy-bundle_start_0" [ style=bold color="green" fontcolor="orange"] "ip-192.168.122.247_monitor_0 undercloud" -> "ip-192.168.122.247_start_0 undercloud" [ style = bold] "ip-192.168.122.247_monitor_0 undercloud" [ style=bold color="green" fontcolor="black"] "ip-192.168.122.247_monitor_10000 undercloud" [ style=bold color="green" fontcolor="black"] "ip-192.168.122.247_start_0 undercloud" -> "haproxy-bundle_start_0" [ style = bold] "ip-192.168.122.247_start_0 undercloud" -> "ip-192.168.122.247_monitor_10000 undercloud" [ style = bold] "ip-192.168.122.247_start_0 undercloud" [ style=bold color="green" fontcolor="black"] "ip-192.168.122.248_monitor_0 undercloud" -> "ip-192.168.122.248_start_0 undercloud" [ style = bold] "ip-192.168.122.248_monitor_0 undercloud" [ style=bold color="green" fontcolor="black"] "ip-192.168.122.248_monitor_10000 undercloud" [ style=bold color="green" fontcolor="black"] "ip-192.168.122.248_start_0 undercloud" -> "haproxy-bundle_start_0" [ style = bold] "ip-192.168.122.248_start_0 undercloud" -> "ip-192.168.122.248_monitor_10000 undercloud" [ style = bold] "ip-192.168.122.248_start_0 undercloud" [ style=bold color="green" fontcolor="black"] "ip-192.168.122.249_monitor_0 undercloud" -> "ip-192.168.122.249_start_0 undercloud" [ style = bold] "ip-192.168.122.249_monitor_0 undercloud" [ style=bold color="green" fontcolor="black"] "ip-192.168.122.249_monitor_10000 undercloud" [ style=bold color="green" fontcolor="black"] "ip-192.168.122.249_start_0 undercloud" -> "haproxy-bundle_start_0" [ style = bold] "ip-192.168.122.249_start_0 undercloud" -> "ip-192.168.122.249_monitor_10000 undercloud" [ style = bold] "ip-192.168.122.249_start_0 undercloud" [ style=bold color="green" fontcolor="black"] "ip-192.168.122.250_monitor_0 undercloud" -> "ip-192.168.122.250_start_0 undercloud" [ style = bold] "ip-192.168.122.250_monitor_0 undercloud" [ style=bold color="green" fontcolor="black"] "ip-192.168.122.250_monitor_10000 undercloud" [ style=bold color="green" fontcolor="black"] "ip-192.168.122.250_start_0 undercloud" -> "haproxy-bundle_start_0" [ style = bold] "ip-192.168.122.250_start_0 undercloud" -> "ip-192.168.122.250_monitor_10000 undercloud" [ style = bold] "ip-192.168.122.250_start_0 undercloud" [ style=bold color="green" fontcolor="black"] "ip-192.168.122.253_monitor_0 undercloud" -> "ip-192.168.122.253_start_0 undercloud" [ style = bold] "ip-192.168.122.253_monitor_0 undercloud" [ style=bold color="green" fontcolor="black"] "ip-192.168.122.253_monitor_10000 undercloud" [ style=bold color="green" fontcolor="black"] "ip-192.168.122.253_start_0 undercloud" -> "haproxy-bundle_start_0" [ style = bold] "ip-192.168.122.253_start_0 undercloud" -> "ip-192.168.122.253_monitor_10000 undercloud" [ style = bold] "ip-192.168.122.253_start_0 undercloud" [ style=bold color="green" fontcolor="black"] "ip-192.168.122.254_monitor_0 undercloud" -> "ip-192.168.122.254_start_0 undercloud" [ style = bold] "ip-192.168.122.254_monitor_0 undercloud" [ style=bold color="green" fontcolor="black"] "ip-192.168.122.254_monitor_10000 undercloud" [ style=bold color="green" fontcolor="black"] "ip-192.168.122.254_start_0 undercloud" -> "haproxy-bundle_start_0" [ style = bold] "ip-192.168.122.254_start_0 undercloud" -> "ip-192.168.122.254_monitor_10000 undercloud" [ style = bold] "ip-192.168.122.254_start_0 undercloud" [ style=bold color="green" fontcolor="black"] "openstack-cinder-volume-docker-0_monitor_0 undercloud" -> "openstack-cinder-volume-docker-0_start_0 undercloud" [ style = bold] "openstack-cinder-volume-docker-0_monitor_0 undercloud" [ style=bold color="green" fontcolor="black"] "openstack-cinder-volume-docker-0_monitor_60000 undercloud" [ style=bold color="green" fontcolor="black"] "openstack-cinder-volume-docker-0_start_0 undercloud" -> "openstack-cinder-volume-docker-0_monitor_60000 undercloud" [ style = bold] "openstack-cinder-volume-docker-0_start_0 undercloud" -> "openstack-cinder-volume_running_0" [ style = bold] "openstack-cinder-volume-docker-0_start_0 undercloud" [ style=bold color="green" fontcolor="black"] "openstack-cinder-volume_running_0" [ style=bold color="green" fontcolor="orange"] "openstack-cinder-volume_start_0" -> "openstack-cinder-volume-docker-0_start_0 undercloud" [ style = bold] "openstack-cinder-volume_start_0" [ style=bold color="green" fontcolor="orange"] "rabbitmq-bundle-0_monitor_60000 undercloud" [ style=bold color="green" fontcolor="black"] "rabbitmq-bundle-0_start_0 undercloud" -> "rabbitmq-bundle-0_monitor_60000 undercloud" [ style = bold] "rabbitmq-bundle-0_start_0 undercloud" -> "rabbitmq:0_monitor_10000 rabbitmq-bundle-0" [ style = bold] "rabbitmq-bundle-0_start_0 undercloud" -> "rabbitmq:0_start_0 rabbitmq-bundle-0" [ style = bold] "rabbitmq-bundle-0_start_0 undercloud" [ style=bold color="green" fontcolor="black"] "rabbitmq-bundle-clone_running_0" -> "rabbitmq-bundle_running_0" [ style = bold] "rabbitmq-bundle-clone_running_0" [ style=bold color="green" fontcolor="orange"] "rabbitmq-bundle-clone_start_0" -> "rabbitmq-bundle-clone_running_0" [ style = bold] "rabbitmq-bundle-clone_start_0" -> "rabbitmq:0_start_0 rabbitmq-bundle-0" [ style = bold] "rabbitmq-bundle-clone_start_0" [ style=bold color="green" fontcolor="orange"] "rabbitmq-bundle-docker-0_monitor_0 undercloud" -> "rabbitmq-bundle-docker-0_start_0 undercloud" [ style = bold] "rabbitmq-bundle-docker-0_monitor_0 undercloud" [ style=bold color="green" fontcolor="black"] "rabbitmq-bundle-docker-0_monitor_60000 undercloud" [ style=bold color="green" fontcolor="black"] "rabbitmq-bundle-docker-0_start_0 undercloud" -> "rabbitmq-bundle-0_start_0 undercloud" [ style = bold] "rabbitmq-bundle-docker-0_start_0 undercloud" -> "rabbitmq-bundle-docker-0_monitor_60000 undercloud" [ style = bold] "rabbitmq-bundle-docker-0_start_0 undercloud" -> "rabbitmq:0_start_0 rabbitmq-bundle-0" [ style = bold] "rabbitmq-bundle-docker-0_start_0 undercloud" [ style=bold color="green" fontcolor="black"] "rabbitmq-bundle_running_0" [ style=bold color="green" fontcolor="orange"] "rabbitmq-bundle_start_0" -> "rabbitmq-bundle-clone_start_0" [ style = bold] "rabbitmq-bundle_start_0" -> "rabbitmq-bundle-docker-0_start_0 undercloud" [ style = bold] "rabbitmq-bundle_start_0" [ style=bold color="green" fontcolor="orange"] "rabbitmq:0_monitor_10000 rabbitmq-bundle-0" [ style=bold color="green" fontcolor="black"] "rabbitmq:0_start_0 rabbitmq-bundle-0" -> "rabbitmq-bundle-clone_running_0" [ style = bold] "rabbitmq:0_start_0 rabbitmq-bundle-0" -> "rabbitmq:0_monitor_10000 rabbitmq-bundle-0" [ style = bold] "rabbitmq:0_start_0 rabbitmq-bundle-0" [ style=bold color="green" fontcolor="black"] "redis-bundle-0_monitor_60000 undercloud" [ style=bold color="green" fontcolor="black"] "redis-bundle-0_start_0 undercloud" -> "redis-bundle-0_monitor_60000 undercloud" [ style = bold] "redis-bundle-0_start_0 undercloud" -> "redis:0_monitor_45000 redis-bundle-0" [ style = bold] "redis-bundle-0_start_0 undercloud" -> "redis:0_monitor_60000 redis-bundle-0" [ style = bold] "redis-bundle-0_start_0 undercloud" -> "redis:0_start_0 redis-bundle-0" [ style = bold] "redis-bundle-0_start_0 undercloud" [ style=bold color="green" fontcolor="black"] "redis-bundle-docker-0_monitor_0 undercloud" -> "redis-bundle-docker-0_start_0 undercloud" [ style = bold] "redis-bundle-docker-0_monitor_0 undercloud" [ style=bold color="green" fontcolor="black"] "redis-bundle-docker-0_monitor_60000 undercloud" [ style=bold color="green" fontcolor="black"] +"redis-bundle-docker-0_start_0 undercloud" -> "galera-bundle-docker-0_start_0 undercloud" [ style = bold] "redis-bundle-docker-0_start_0 undercloud" -> "redis-bundle-0_start_0 undercloud" [ style = bold] "redis-bundle-docker-0_start_0 undercloud" -> "redis-bundle-docker-0_monitor_60000 undercloud" [ style = bold] "redis-bundle-docker-0_start_0 undercloud" -> "redis:0_start_0 redis-bundle-0" [ style = bold] "redis-bundle-docker-0_start_0 undercloud" [ style=bold color="green" fontcolor="black"] "redis-bundle-master_running_0" -> "redis-bundle_running_0" [ style = bold] "redis-bundle-master_running_0" [ style=bold color="green" fontcolor="orange"] "redis-bundle-master_start_0" -> "redis-bundle-master_running_0" [ style = bold] "redis-bundle-master_start_0" -> "redis:0_start_0 redis-bundle-0" [ style = bold] "redis-bundle-master_start_0" [ style=bold color="green" fontcolor="orange"] "redis-bundle_running_0" -> "galera-bundle_start_0" [ style = bold] "redis-bundle_running_0" [ style=bold color="green" fontcolor="orange"] "redis-bundle_start_0" -> "redis-bundle-docker-0_start_0 undercloud" [ style = bold] "redis-bundle_start_0" -> "redis-bundle-master_start_0" [ style = bold] "redis-bundle_start_0" [ style=bold color="green" fontcolor="orange"] "redis:0_monitor_45000 redis-bundle-0" [ style=bold color="green" fontcolor="black"] "redis:0_monitor_60000 redis-bundle-0" [ style=bold color="green" fontcolor="black"] "redis:0_start_0 redis-bundle-0" -> "redis-bundle-master_running_0" [ style = bold] "redis:0_start_0 redis-bundle-0" -> "redis:0_monitor_45000 redis-bundle-0" [ style = bold] "redis:0_start_0 redis-bundle-0" -> "redis:0_monitor_60000 redis-bundle-0" [ style = bold] "redis:0_start_0 redis-bundle-0" [ style=bold color="green" fontcolor="black"] } diff --git a/pengine/test10/bundle-order-startup.exp b/pengine/test10/bundle-order-startup.exp index 51d108d9dc..66ecb0827e 100644 --- a/pengine/test10/bundle-order-startup.exp +++ b/pengine/test10/bundle-order-startup.exp @@ -1,825 +1,831 @@ + + + + + + diff --git a/pengine/test10/bundle-order-stop.dot b/pengine/test10/bundle-order-stop.dot index 235b634585..af2493c251 100644 --- a/pengine/test10/bundle-order-stop.dot +++ b/pengine/test10/bundle-order-stop.dot @@ -1,194 +1,207 @@ digraph "g" { "Cancel galera_monitor_10000 galera-bundle-0" -> "galera_demote_0 galera-bundle-0" [ style = bold] "Cancel galera_monitor_10000 galera-bundle-0" [ style=bold color="green" fontcolor="black"] "Cancel redis_monitor_20000 redis-bundle-0" -> "redis_demote_0 redis-bundle-0" [ style = bold] "Cancel redis_monitor_20000 redis-bundle-0" [ style=bold color="green" fontcolor="black"] "all_stopped" [ style=bold color="green" fontcolor="orange"] "do_shutdown undercloud" [ style=bold color="green" fontcolor="black"] "galera-bundle-0_stop_0 undercloud" -> "all_stopped" [ style = bold] "galera-bundle-0_stop_0 undercloud" -> "do_shutdown undercloud" [ style = bold] "galera-bundle-0_stop_0 undercloud" -> "galera-bundle-docker-0_stop_0 undercloud" [ style = bold] "galera-bundle-0_stop_0 undercloud" [ style=bold color="green" fontcolor="black"] "galera-bundle-docker-0_stop_0 undercloud" -> "all_stopped" [ style = bold] "galera-bundle-docker-0_stop_0 undercloud" -> "do_shutdown undercloud" [ style = bold] +"galera-bundle-docker-0_stop_0 undercloud" -> "redis-bundle-docker-0_stop_0 undercloud" [ style = bold] "galera-bundle-docker-0_stop_0 undercloud" [ style=bold color="green" fontcolor="black"] "galera-bundle-master_demote_0" -> "galera-bundle-master_demoted_0" [ style = bold] "galera-bundle-master_demote_0" -> "galera_demote_0 galera-bundle-0" [ style = bold] "galera-bundle-master_demote_0" [ style=bold color="green" fontcolor="orange"] "galera-bundle-master_demoted_0" -> "galera-bundle-master_start_0" [ style = dashed] "galera-bundle-master_demoted_0" -> "galera-bundle-master_stop_0" [ style = bold] "galera-bundle-master_demoted_0" -> "galera-bundle_demoted_0" [ style = bold] "galera-bundle-master_demoted_0" [ style=bold color="green" fontcolor="orange"] +"galera-bundle-master_running_0" -> "galera-bundle_running_0" [ style = dashed] "galera-bundle-master_running_0" [ style=dashed color="red" fontcolor="orange"] "galera-bundle-master_start_0" -> "galera-bundle-master_running_0" [ style = dashed] "galera-bundle-master_start_0" -> "galera_start_0 galera-bundle-0" [ style = dashed] "galera-bundle-master_start_0" [ style=dashed color="red" fontcolor="orange"] "galera-bundle-master_stop_0" -> "galera-bundle-master_stopped_0" [ style = bold] "galera-bundle-master_stop_0" -> "galera_stop_0 galera-bundle-0" [ style = bold] "galera-bundle-master_stop_0" [ style=bold color="green" fontcolor="orange"] "galera-bundle-master_stopped_0" -> "galera-bundle-master_start_0" [ style = dashed] "galera-bundle-master_stopped_0" -> "galera-bundle_stopped_0" [ style = bold] "galera-bundle-master_stopped_0" [ style=bold color="green" fontcolor="orange"] "galera-bundle_demote_0" -> "galera-bundle-master_demote_0" [ style = bold] "galera-bundle_demote_0" -> "galera-bundle_demoted_0" [ style = bold] "galera-bundle_demote_0" [ style=bold color="green" fontcolor="orange"] +"galera-bundle_demoted_0" -> "galera-bundle_start_0" [ style = dashed] "galera-bundle_demoted_0" -> "galera-bundle_stop_0" [ style = bold] "galera-bundle_demoted_0" [ style=bold color="green" fontcolor="orange"] +"galera-bundle_running_0" [ style=dashed color="red" fontcolor="orange"] +"galera-bundle_start_0" -> "galera-bundle-master_start_0" [ style = dashed] +"galera-bundle_start_0" [ style=dashed color="red" fontcolor="orange"] "galera-bundle_stop_0" -> "galera-bundle-master_stop_0" [ style = bold] "galera-bundle_stop_0" -> "galera_stop_0 galera-bundle-0" [ style = bold] "galera-bundle_stop_0" [ style=bold color="green" fontcolor="orange"] +"galera-bundle_stopped_0" -> "galera-bundle_start_0" [ style = dashed] "galera-bundle_stopped_0" -> "redis-bundle_stop_0" [ style = bold] "galera-bundle_stopped_0" [ style=bold color="green" fontcolor="orange"] "galera_demote_0 galera-bundle-0" -> "galera-bundle-0_stop_0 undercloud" [ style = bold] "galera_demote_0 galera-bundle-0" -> "galera-bundle-master_demoted_0" [ style = bold] "galera_demote_0 galera-bundle-0" -> "galera_monitor_20000 galera-bundle-0" [ style = dashed] "galera_demote_0 galera-bundle-0" -> "galera_monitor_30000 galera-bundle-0" [ style = dashed] "galera_demote_0 galera-bundle-0" -> "galera_stop_0 galera-bundle-0" [ style = bold] "galera_demote_0 galera-bundle-0" [ style=bold color="green" fontcolor="black"] "galera_monitor_20000 galera-bundle-0" [ style=dashed color="red" fontcolor="black"] "galera_monitor_30000 galera-bundle-0" [ style=dashed color="red" fontcolor="black"] "galera_start_0 galera-bundle-0" -> "galera-bundle-master_running_0" [ style = dashed] "galera_start_0 galera-bundle-0" -> "galera_monitor_20000 galera-bundle-0" [ style = dashed] "galera_start_0 galera-bundle-0" -> "galera_monitor_30000 galera-bundle-0" [ style = dashed] "galera_start_0 galera-bundle-0" [ style=dashed color="red" fontcolor="black"] "galera_stop_0 galera-bundle-0" -> "all_stopped" [ style = bold] "galera_stop_0 galera-bundle-0" -> "galera-bundle-0_stop_0 undercloud" [ style = bold] "galera_stop_0 galera-bundle-0" -> "galera-bundle-master_stopped_0" [ style = bold] "galera_stop_0 galera-bundle-0" -> "galera_start_0 galera-bundle-0" [ style = dashed] "galera_stop_0 galera-bundle-0" [ style=bold color="green" fontcolor="black"] "haproxy-bundle-docker-0_stop_0 undercloud" -> "all_stopped" [ style = bold] "haproxy-bundle-docker-0_stop_0 undercloud" -> "do_shutdown undercloud" [ style = bold] "haproxy-bundle-docker-0_stop_0 undercloud" -> "haproxy-bundle_stopped_0" [ style = bold] "haproxy-bundle-docker-0_stop_0 undercloud" [ style=bold color="green" fontcolor="black"] "haproxy-bundle_stop_0" -> "haproxy-bundle-docker-0_stop_0 undercloud" [ style = bold] "haproxy-bundle_stop_0" [ style=bold color="green" fontcolor="orange"] "haproxy-bundle_stopped_0" -> "ip-192.168.122.247_stop_0 undercloud" [ style = bold] "haproxy-bundle_stopped_0" -> "ip-192.168.122.248_stop_0 undercloud" [ style = bold] "haproxy-bundle_stopped_0" -> "ip-192.168.122.249_stop_0 undercloud" [ style = bold] "haproxy-bundle_stopped_0" -> "ip-192.168.122.250_stop_0 undercloud" [ style = bold] "haproxy-bundle_stopped_0" -> "ip-192.168.122.253_stop_0 undercloud" [ style = bold] "haproxy-bundle_stopped_0" -> "ip-192.168.122.254_stop_0 undercloud" [ style = bold] "haproxy-bundle_stopped_0" [ style=bold color="green" fontcolor="orange"] "ip-192.168.122.247_start_0 " [ style=dashed color="red" fontcolor="black"] "ip-192.168.122.247_stop_0 undercloud" -> "all_stopped" [ style = bold] "ip-192.168.122.247_stop_0 undercloud" -> "do_shutdown undercloud" [ style = bold] "ip-192.168.122.247_stop_0 undercloud" -> "ip-192.168.122.247_start_0 " [ style = dashed] "ip-192.168.122.247_stop_0 undercloud" [ style=bold color="green" fontcolor="black"] "ip-192.168.122.248_start_0 " [ style=dashed color="red" fontcolor="black"] "ip-192.168.122.248_stop_0 undercloud" -> "all_stopped" [ style = bold] "ip-192.168.122.248_stop_0 undercloud" -> "do_shutdown undercloud" [ style = bold] "ip-192.168.122.248_stop_0 undercloud" -> "ip-192.168.122.248_start_0 " [ style = dashed] "ip-192.168.122.248_stop_0 undercloud" [ style=bold color="green" fontcolor="black"] "ip-192.168.122.249_start_0 " [ style=dashed color="red" fontcolor="black"] "ip-192.168.122.249_stop_0 undercloud" -> "all_stopped" [ style = bold] "ip-192.168.122.249_stop_0 undercloud" -> "do_shutdown undercloud" [ style = bold] "ip-192.168.122.249_stop_0 undercloud" -> "ip-192.168.122.249_start_0 " [ style = dashed] "ip-192.168.122.249_stop_0 undercloud" [ style=bold color="green" fontcolor="black"] "ip-192.168.122.250_start_0 " [ style=dashed color="red" fontcolor="black"] "ip-192.168.122.250_stop_0 undercloud" -> "all_stopped" [ style = bold] "ip-192.168.122.250_stop_0 undercloud" -> "do_shutdown undercloud" [ style = bold] "ip-192.168.122.250_stop_0 undercloud" -> "ip-192.168.122.250_start_0 " [ style = dashed] "ip-192.168.122.250_stop_0 undercloud" [ style=bold color="green" fontcolor="black"] "ip-192.168.122.253_start_0 " [ style=dashed color="red" fontcolor="black"] "ip-192.168.122.253_stop_0 undercloud" -> "all_stopped" [ style = bold] "ip-192.168.122.253_stop_0 undercloud" -> "do_shutdown undercloud" [ style = bold] "ip-192.168.122.253_stop_0 undercloud" -> "ip-192.168.122.253_start_0 " [ style = dashed] "ip-192.168.122.253_stop_0 undercloud" [ style=bold color="green" fontcolor="black"] "ip-192.168.122.254_start_0 " [ style=dashed color="red" fontcolor="black"] "ip-192.168.122.254_stop_0 undercloud" -> "all_stopped" [ style = bold] "ip-192.168.122.254_stop_0 undercloud" -> "do_shutdown undercloud" [ style = bold] "ip-192.168.122.254_stop_0 undercloud" -> "ip-192.168.122.254_start_0 " [ style = dashed] "ip-192.168.122.254_stop_0 undercloud" [ style=bold color="green" fontcolor="black"] "openstack-cinder-volume-docker-0_stop_0 undercloud" -> "all_stopped" [ style = bold] "openstack-cinder-volume-docker-0_stop_0 undercloud" -> "do_shutdown undercloud" [ style = bold] "openstack-cinder-volume-docker-0_stop_0 undercloud" -> "openstack-cinder-volume_stopped_0" [ style = bold] "openstack-cinder-volume-docker-0_stop_0 undercloud" [ style=bold color="green" fontcolor="black"] "openstack-cinder-volume_stop_0" -> "openstack-cinder-volume-docker-0_stop_0 undercloud" [ style = bold] "openstack-cinder-volume_stop_0" [ style=bold color="green" fontcolor="orange"] "openstack-cinder-volume_stopped_0" [ style=bold color="green" fontcolor="orange"] "rabbitmq-bundle-0_stop_0 undercloud" -> "all_stopped" [ style = bold] "rabbitmq-bundle-0_stop_0 undercloud" -> "do_shutdown undercloud" [ style = bold] "rabbitmq-bundle-0_stop_0 undercloud" -> "rabbitmq-bundle-docker-0_stop_0 undercloud" [ style = bold] "rabbitmq-bundle-0_stop_0 undercloud" [ style=bold color="green" fontcolor="black"] +"rabbitmq-bundle-clone_running_0" -> "rabbitmq-bundle_running_0" [ style = dashed] "rabbitmq-bundle-clone_running_0" [ style=dashed color="red" fontcolor="orange"] "rabbitmq-bundle-clone_start_0" -> "rabbitmq-bundle-clone_running_0" [ style = dashed] "rabbitmq-bundle-clone_start_0" -> "rabbitmq_start_0 rabbitmq-bundle-0" [ style = dashed] "rabbitmq-bundle-clone_start_0" [ style=dashed color="red" fontcolor="orange"] "rabbitmq-bundle-clone_stop_0" -> "rabbitmq-bundle-clone_stopped_0" [ style = bold] "rabbitmq-bundle-clone_stop_0" -> "rabbitmq_stop_0 rabbitmq-bundle-0" [ style = bold] "rabbitmq-bundle-clone_stop_0" [ style=bold color="green" fontcolor="orange"] "rabbitmq-bundle-clone_stopped_0" -> "rabbitmq-bundle-clone_start_0" [ style = dashed] "rabbitmq-bundle-clone_stopped_0" -> "rabbitmq-bundle_stopped_0" [ style = bold] "rabbitmq-bundle-clone_stopped_0" [ style=bold color="green" fontcolor="orange"] "rabbitmq-bundle-docker-0_stop_0 undercloud" -> "all_stopped" [ style = bold] "rabbitmq-bundle-docker-0_stop_0 undercloud" -> "do_shutdown undercloud" [ style = bold] "rabbitmq-bundle-docker-0_stop_0 undercloud" [ style=bold color="green" fontcolor="black"] +"rabbitmq-bundle_running_0" [ style=dashed color="red" fontcolor="orange"] "rabbitmq-bundle_stop_0" -> "rabbitmq-bundle-clone_stop_0" [ style = bold] "rabbitmq-bundle_stop_0" -> "rabbitmq_stop_0 rabbitmq-bundle-0" [ style = bold] "rabbitmq-bundle_stop_0" [ style=bold color="green" fontcolor="orange"] "rabbitmq-bundle_stopped_0" [ style=bold color="green" fontcolor="orange"] "rabbitmq_monitor_10000 rabbitmq-bundle-0" [ style=dashed color="red" fontcolor="black"] "rabbitmq_start_0 rabbitmq-bundle-0" -> "rabbitmq-bundle-clone_running_0" [ style = dashed] "rabbitmq_start_0 rabbitmq-bundle-0" -> "rabbitmq_monitor_10000 rabbitmq-bundle-0" [ style = dashed] "rabbitmq_start_0 rabbitmq-bundle-0" [ style=dashed color="red" fontcolor="black"] "rabbitmq_stop_0 rabbitmq-bundle-0" -> "all_stopped" [ style = bold] "rabbitmq_stop_0 rabbitmq-bundle-0" -> "rabbitmq-bundle-0_stop_0 undercloud" [ style = bold] "rabbitmq_stop_0 rabbitmq-bundle-0" -> "rabbitmq-bundle-clone_stopped_0" [ style = bold] "rabbitmq_stop_0 rabbitmq-bundle-0" -> "rabbitmq_start_0 rabbitmq-bundle-0" [ style = dashed] "rabbitmq_stop_0 rabbitmq-bundle-0" [ style=bold color="green" fontcolor="black"] "redis-bundle-0_stop_0 undercloud" -> "all_stopped" [ style = bold] "redis-bundle-0_stop_0 undercloud" -> "do_shutdown undercloud" [ style = bold] "redis-bundle-0_stop_0 undercloud" -> "redis-bundle-docker-0_stop_0 undercloud" [ style = bold] "redis-bundle-0_stop_0 undercloud" [ style=bold color="green" fontcolor="black"] "redis-bundle-docker-0_stop_0 undercloud" -> "all_stopped" [ style = bold] "redis-bundle-docker-0_stop_0 undercloud" -> "do_shutdown undercloud" [ style = bold] +"redis-bundle-docker-0_stop_0 undercloud" -> "haproxy-bundle-docker-0_stop_0 undercloud" [ style = bold] "redis-bundle-docker-0_stop_0 undercloud" [ style=bold color="green" fontcolor="black"] "redis-bundle-master_demote_0" -> "redis-bundle-master_demoted_0" [ style = bold] "redis-bundle-master_demote_0" -> "redis_demote_0 redis-bundle-0" [ style = bold] "redis-bundle-master_demote_0" [ style=bold color="green" fontcolor="orange"] "redis-bundle-master_demoted_0" -> "redis-bundle-master_start_0" [ style = dashed] "redis-bundle-master_demoted_0" -> "redis-bundle-master_stop_0" [ style = bold] "redis-bundle-master_demoted_0" -> "redis-bundle_demoted_0" [ style = bold] "redis-bundle-master_demoted_0" [ style=bold color="green" fontcolor="orange"] +"redis-bundle-master_running_0" -> "redis-bundle_running_0" [ style = dashed] "redis-bundle-master_running_0" [ style=dashed color="red" fontcolor="orange"] "redis-bundle-master_start_0" -> "redis-bundle-master_running_0" [ style = dashed] "redis-bundle-master_start_0" -> "redis_start_0 redis-bundle-0" [ style = dashed] "redis-bundle-master_start_0" [ style=dashed color="red" fontcolor="orange"] "redis-bundle-master_stop_0" -> "redis-bundle-master_stopped_0" [ style = bold] "redis-bundle-master_stop_0" -> "redis_stop_0 redis-bundle-0" [ style = bold] "redis-bundle-master_stop_0" [ style=bold color="green" fontcolor="orange"] "redis-bundle-master_stopped_0" -> "redis-bundle-master_start_0" [ style = dashed] "redis-bundle-master_stopped_0" -> "redis-bundle_stopped_0" [ style = bold] "redis-bundle-master_stopped_0" [ style=bold color="green" fontcolor="orange"] "redis-bundle_demote_0" -> "redis-bundle-master_demote_0" [ style = bold] "redis-bundle_demote_0" -> "redis-bundle_demoted_0" [ style = bold] "redis-bundle_demote_0" [ style=bold color="green" fontcolor="orange"] -"redis-bundle_demoted_0" -> "redis-bundle_start_0" [ style = bold] +"redis-bundle_demoted_0" -> "redis-bundle_start_0" [ style = dashed] "redis-bundle_demoted_0" -> "redis-bundle_stop_0" [ style = bold] "redis-bundle_demoted_0" [ style=bold color="green" fontcolor="orange"] +"redis-bundle_running_0" -> "galera-bundle_start_0" [ style = dashed] +"redis-bundle_running_0" [ style=dashed color="red" fontcolor="orange"] "redis-bundle_start_0" -> "redis-bundle-master_start_0" [ style = dashed] -"redis-bundle_start_0" [ style=bold color="green" fontcolor="orange"] +"redis-bundle_start_0" [ style=dashed color="red" fontcolor="orange"] "redis-bundle_stop_0" -> "redis-bundle-master_stop_0" [ style = bold] "redis-bundle_stop_0" -> "redis_stop_0 redis-bundle-0" [ style = bold] "redis-bundle_stop_0" [ style=bold color="green" fontcolor="orange"] "redis-bundle_stopped_0" -> "haproxy-bundle_stop_0" [ style = bold] -"redis-bundle_stopped_0" -> "redis-bundle_start_0" [ style = bold] +"redis-bundle_stopped_0" -> "redis-bundle_start_0" [ style = dashed] "redis-bundle_stopped_0" [ style=bold color="green" fontcolor="orange"] "redis_demote_0 redis-bundle-0" -> "redis-bundle-0_stop_0 undercloud" [ style = bold] "redis_demote_0 redis-bundle-0" -> "redis-bundle-master_demoted_0" [ style = bold] "redis_demote_0 redis-bundle-0" -> "redis_monitor_45000 redis-bundle-0" [ style = dashed] "redis_demote_0 redis-bundle-0" -> "redis_monitor_60000 redis-bundle-0" [ style = dashed] "redis_demote_0 redis-bundle-0" -> "redis_stop_0 redis-bundle-0" [ style = bold] "redis_demote_0 redis-bundle-0" [ style=bold color="green" fontcolor="black"] "redis_monitor_45000 redis-bundle-0" [ style=dashed color="red" fontcolor="black"] "redis_monitor_60000 redis-bundle-0" [ style=dashed color="red" fontcolor="black"] "redis_start_0 redis-bundle-0" -> "redis-bundle-master_running_0" [ style = dashed] "redis_start_0 redis-bundle-0" -> "redis_monitor_45000 redis-bundle-0" [ style = dashed] "redis_start_0 redis-bundle-0" -> "redis_monitor_60000 redis-bundle-0" [ style = dashed] "redis_start_0 redis-bundle-0" [ style=dashed color="red" fontcolor="black"] "redis_stop_0 redis-bundle-0" -> "all_stopped" [ style = bold] "redis_stop_0 redis-bundle-0" -> "redis-bundle-0_stop_0 undercloud" [ style = bold] "redis_stop_0 redis-bundle-0" -> "redis-bundle-master_stopped_0" [ style = bold] "redis_stop_0 redis-bundle-0" -> "redis_start_0 redis-bundle-0" [ style = dashed] "redis_stop_0 redis-bundle-0" [ style=bold color="green" fontcolor="black"] } diff --git a/pengine/test10/bundle-order-stop.exp b/pengine/test10/bundle-order-stop.exp index d0854331d8..02a73722cf 100644 --- a/pengine/test10/bundle-order-stop.exp +++ b/pengine/test10/bundle-order-stop.exp @@ -1,734 +1,725 @@ + + + + + + - - - - - - - - - - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/pengine/test10/bundle-order-stop.summary b/pengine/test10/bundle-order-stop.summary index bd6f937515..c341f4c635 100644 --- a/pengine/test10/bundle-order-stop.summary +++ b/pengine/test10/bundle-order-stop.summary @@ -1,114 +1,113 @@ Current cluster status: Online: [ undercloud ] Containers: [ galera-bundle-0:galera-bundle-docker-0 rabbitmq-bundle-0:rabbitmq-bundle-docker-0 redis-bundle-0:redis-bundle-docker-0 ] Docker container: rabbitmq-bundle [192.168.24.1:8787/tripleoupstream/centos-binary-rabbitmq:latest] rabbitmq-bundle-0 (ocf::heartbeat:rabbitmq-cluster): Started undercloud Docker container: galera-bundle [192.168.24.1:8787/tripleoupstream/centos-binary-mariadb:latest] galera-bundle-0 (ocf::heartbeat:galera): Master undercloud Docker container: redis-bundle [192.168.24.1:8787/tripleoupstream/centos-binary-redis:latest] redis-bundle-0 (ocf::heartbeat:redis): Master undercloud ip-192.168.122.254 (ocf::heartbeat:IPaddr2): Started undercloud ip-192.168.122.250 (ocf::heartbeat:IPaddr2): Started undercloud ip-192.168.122.249 (ocf::heartbeat:IPaddr2): Started undercloud ip-192.168.122.253 (ocf::heartbeat:IPaddr2): Started undercloud ip-192.168.122.247 (ocf::heartbeat:IPaddr2): Started undercloud ip-192.168.122.248 (ocf::heartbeat:IPaddr2): Started undercloud Docker container: haproxy-bundle [192.168.24.1:8787/tripleoupstream/centos-binary-haproxy:latest] haproxy-bundle-docker-0 (ocf::heartbeat:docker): Started undercloud Docker container: openstack-cinder-volume [192.168.24.1:8787/tripleoupstream/centos-binary-cinder-volume:latest] openstack-cinder-volume-docker-0 (ocf::heartbeat:docker): Started undercloud Transition Summary: * Shutdown undercloud * Stop rabbitmq-bundle-docker-0 (undercloud) * Stop rabbitmq-bundle-0 (undercloud) * Stop rabbitmq:0 (Started rabbitmq-bundle-0) * Stop galera-bundle-docker-0 (undercloud) * Stop galera-bundle-0 (undercloud) * Demote galera:0 (Master -> Slave galera-bundle-0) * Restart galera:0 (Slave galera-bundle-0) * Stop redis-bundle-docker-0 (undercloud) * Stop redis-bundle-0 (undercloud) * Demote redis:0 (Master -> Slave redis-bundle-0) * Restart redis:0 (Slave redis-bundle-0) * Stop ip-192.168.122.254 (undercloud) * Stop ip-192.168.122.250 (undercloud) * Stop ip-192.168.122.249 (undercloud) * Stop ip-192.168.122.253 (undercloud) * Stop ip-192.168.122.247 (undercloud) * Stop ip-192.168.122.248 (undercloud) * Stop haproxy-bundle-docker-0 (undercloud) * Stop openstack-cinder-volume-docker-0 (undercloud) Executing cluster transition: * Resource action: galera cancel=10000 on galera-bundle-0 * Resource action: redis cancel=20000 on redis-bundle-0 * Pseudo action: openstack-cinder-volume_stop_0 * Pseudo action: redis-bundle_demote_0 * Pseudo action: redis-bundle-master_demote_0 * Pseudo action: galera-bundle_demote_0 * Pseudo action: galera-bundle-master_demote_0 * Pseudo action: rabbitmq-bundle_stop_0 * Resource action: galera demote on galera-bundle-0 * Resource action: redis demote on redis-bundle-0 * Resource action: openstack-cinder-volume-docker-0 stop on undercloud * Pseudo action: openstack-cinder-volume_stopped_0 * Pseudo action: redis-bundle-master_demoted_0 * Pseudo action: galera-bundle-master_demoted_0 * Pseudo action: rabbitmq-bundle-clone_stop_0 * Resource action: rabbitmq stop on rabbitmq-bundle-0 * Resource action: rabbitmq-bundle-0 stop on undercloud * Pseudo action: redis-bundle_demoted_0 * Pseudo action: galera-bundle_demoted_0 * Pseudo action: galera-bundle_stop_0 * Pseudo action: rabbitmq-bundle-clone_stopped_0 * Pseudo action: rabbitmq-bundle_stopped_0 * Resource action: rabbitmq-bundle-docker-0 stop on undercloud * Pseudo action: galera-bundle-master_stop_0 * Resource action: galera stop on galera-bundle-0 * Resource action: galera-bundle-0 stop on undercloud * Pseudo action: galera-bundle-master_stopped_0 * Pseudo action: galera-bundle_stopped_0 * Resource action: galera-bundle-docker-0 stop on undercloud * Pseudo action: redis-bundle_stop_0 * Pseudo action: redis-bundle-master_stop_0 * Resource action: redis stop on redis-bundle-0 * Resource action: redis-bundle-0 stop on undercloud * Pseudo action: redis-bundle-master_stopped_0 * Pseudo action: redis-bundle_stopped_0 - * Pseudo action: redis-bundle_start_0 * Resource action: redis-bundle-docker-0 stop on undercloud * Pseudo action: haproxy-bundle_stop_0 * Resource action: haproxy-bundle-docker-0 stop on undercloud * Pseudo action: haproxy-bundle_stopped_0 * Resource action: ip-192.168.122.254 stop on undercloud * Resource action: ip-192.168.122.250 stop on undercloud * Resource action: ip-192.168.122.249 stop on undercloud * Resource action: ip-192.168.122.253 stop on undercloud * Resource action: ip-192.168.122.247 stop on undercloud * Resource action: ip-192.168.122.248 stop on undercloud * Cluster action: do_shutdown on undercloud * Pseudo action: all_stopped Revised cluster status: Online: [ undercloud ] Docker container: rabbitmq-bundle [192.168.24.1:8787/tripleoupstream/centos-binary-rabbitmq:latest] rabbitmq-bundle-0 (ocf::heartbeat:rabbitmq-cluster): Stopped Docker container: galera-bundle [192.168.24.1:8787/tripleoupstream/centos-binary-mariadb:latest] galera-bundle-0 (ocf::heartbeat:galera): Stopped Docker container: redis-bundle [192.168.24.1:8787/tripleoupstream/centos-binary-redis:latest] redis-bundle-0 (ocf::heartbeat:redis): Stopped ip-192.168.122.254 (ocf::heartbeat:IPaddr2): Stopped ip-192.168.122.250 (ocf::heartbeat:IPaddr2): Stopped ip-192.168.122.249 (ocf::heartbeat:IPaddr2): Stopped ip-192.168.122.253 (ocf::heartbeat:IPaddr2): Stopped ip-192.168.122.247 (ocf::heartbeat:IPaddr2): Stopped ip-192.168.122.248 (ocf::heartbeat:IPaddr2): Stopped Docker container: haproxy-bundle [192.168.24.1:8787/tripleoupstream/centos-binary-haproxy:latest] haproxy-bundle-docker-0 (ocf::heartbeat:docker): Stopped Docker container: openstack-cinder-volume [192.168.24.1:8787/tripleoupstream/centos-binary-cinder-volume:latest] openstack-cinder-volume-docker-0 (ocf::heartbeat:docker): Stopped diff --git a/pengine/utils.h b/pengine/utils.h index 10e7201161..14211664a9 100644 --- a/pengine/utils.h +++ b/pengine/utils.h @@ -1,81 +1,81 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PENGINE_AUTILS__H # define PENGINE_AUTILS__H /* Constraint helper functions */ extern rsc_colocation_t *invert_constraint(rsc_colocation_t * constraint); extern rsc_to_node_t *copy_constraint(rsc_to_node_t * constraint); extern rsc_to_node_t *rsc2node_new(const char *id, resource_t * rsc, int weight, const char *discovery_mode, node_t * node, pe_working_set_t * data_set); extern void pe_free_rsc_to_node(GListPtr constraints); extern void pe_free_ordering(GListPtr constraints); extern gboolean rsc_colocation_new(const char *id, const char *node_attr, int score, resource_t * rsc_lh, resource_t * rsc_rh, const char *state_lh, const char *state_rh, pe_working_set_t * data_set); extern gboolean rsc_ticket_new(const char *id, resource_t * rsc_lh, ticket_t * ticket, const char *state_lh, const char *loss_policy, pe_working_set_t * data_set); extern gint sort_node_weight(gconstpointer a, gconstpointer b, gpointer data_set); extern gboolean can_run_resources(const node_t * node); extern gboolean native_assign_node(resource_t * rsc, GListPtr candidates, node_t * chosen, gboolean force); void native_deallocate(resource_t * rsc); extern void log_action(unsigned int log_level, const char *pre_text, action_t * action, gboolean details); extern gboolean can_run_any(GHashTable * nodes); -extern resource_t *find_compatible_child(resource_t * local_child, resource_t * rsc, +extern resource_t *find_compatible_child(resource_t * local_child, resource_t * rsc, GListPtr children, enum rsc_role_e filter, gboolean current); gboolean is_child_compatible(resource_t *child_rsc, node_t * local_node, enum rsc_role_e filter, gboolean current); bool assign_node(resource_t * rsc, node_t * node, gboolean force); - +enum pe_action_flags summary_action_flags(action_t * action, GListPtr children, node_t * node); enum filter_colocation_res { influence_nothing = 0, influence_rsc_location, influence_rsc_priority, }; extern enum filter_colocation_res filter_colocation_constraint(resource_t * rsc_lh, resource_t * rsc_rh, rsc_colocation_t * constraint, gboolean preview); extern int compare_capacity(const node_t * node1, const node_t * node2); extern void calculate_utilization(GHashTable * current_utilization, GHashTable * utilization, gboolean plus); extern void process_utilization(resource_t * rsc, node_t ** prefer, pe_working_set_t * data_set); pe_action_t *create_pseudo_resource_op(resource_t * rsc, const char *task, bool optional, bool runnable, pe_working_set_t *data_set); # define STONITH_UP "stonith_up" # define STONITH_DONE "stonith_complete" # define ALL_STOPPED "all_stopped" # define LOAD_STOPPED "load_stopped" #endif