diff --git a/lib/pengine/clone.c b/lib/pengine/clone.c index 296970e481..42454f7a72 100644 --- a/lib/pengine/clone.c +++ b/lib/pengine/clone.c @@ -1,497 +1,503 @@ /* * 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.1 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #define VARIANT_CLONE 1 #include "./variant.h" void clone_create_notifications( resource_t *rsc, action_t *action, action_t *action_complete, pe_working_set_t *data_set); void force_non_unique_clone(resource_t *rsc, const char *rid, pe_working_set_t *data_set); resource_t *create_child_clone(resource_t *rsc, int sub_id, pe_working_set_t *data_set); static void mark_as_orphan(resource_t *rsc) { set_bit(rsc->flags, pe_rsc_orphan); slist_iter( child, resource_t, rsc->children, lpc, mark_as_orphan(child); ); } static void clear_bit_recursive(resource_t *rsc, unsigned long long flag) { clear_bit_inplace(rsc->flags, flag); if(rsc->children) { slist_iter( child_rsc, resource_t, rsc->children, lpc, clear_bit_recursive(child_rsc, flag); ); } } void force_non_unique_clone(resource_t *rsc, const char *rid, pe_working_set_t *data_set) { if(rsc->variant == pe_clone || rsc->variant == pe_master) { clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); crm_config_warn("Clones %s contains non-OCF resource %s and so " "can only be used as an anonymous clone. " "Set the "XML_RSC_ATTR_UNIQUE" meta attribute to false", rsc->id, rid); clone_data->clone_node_max = 1; clone_data->clone_max = g_list_length(data_set->nodes); clear_bit_recursive(rsc, pe_rsc_unique); } } resource_t * find_clone_instance(resource_t *rsc, const char *sub_id, pe_working_set_t *data_set) { char *child_id = NULL; resource_t *child = NULL; const char *child_base = NULL; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); child_base = ID(clone_data->xml_obj_child); child_id = crm_concat(child_base, sub_id, ':'); child = pe_find_resource(rsc->children, child_id); crm_free(child_id); return child; } resource_t * create_child_clone(resource_t *rsc, int sub_id, pe_working_set_t *data_set) { gboolean as_orphan = FALSE; char *inc_num = NULL; char *inc_max = NULL; resource_t *child_rsc = NULL; xmlNode * child_copy = NULL; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); CRM_CHECK(clone_data->xml_obj_child != NULL, return FALSE); if(sub_id < 0) { as_orphan = TRUE; sub_id = clone_data->total_clones; } inc_num = crm_itoa(sub_id); inc_max = crm_itoa(clone_data->clone_max); child_copy = copy_xml(clone_data->xml_obj_child); crm_xml_add(child_copy, XML_RSC_ATTR_INCARNATION, inc_num); if(common_unpack(child_copy, &child_rsc, rsc, data_set) == FALSE) { pe_err("Failed unpacking resource %s", crm_element_value(child_copy, XML_ATTR_ID)); child_rsc = NULL; goto bail; } /* child_rsc->globally_unique = rsc->globally_unique; */ clone_data->total_clones += 1; crm_debug_2("Setting clone attributes for: %s", child_rsc->id); rsc->children = g_list_append(rsc->children, child_rsc); if(as_orphan) { mark_as_orphan(child_rsc); } add_hash_param(child_rsc->meta, XML_RSC_ATTR_INCARNATION_MAX, inc_max); print_resource(LOG_DEBUG_3, "Added", child_rsc, FALSE); bail: crm_free(inc_num); crm_free(inc_max); return child_rsc; } gboolean master_unpack(resource_t *rsc, pe_working_set_t *data_set) { const char *master_max = g_hash_table_lookup( rsc->meta, XML_RSC_ATTR_MASTER_MAX); const char *master_node_max = g_hash_table_lookup( rsc->meta, XML_RSC_ATTR_MASTER_NODEMAX); g_hash_table_replace(rsc->meta, crm_strdup("stateful"), crm_strdup(XML_BOOLEAN_TRUE)); if(clone_unpack(rsc, data_set)) { clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); clone_data->master_max = crm_parse_int(master_max, "1"); clone_data->master_node_max = crm_parse_int(master_node_max, "1"); return TRUE; } return FALSE; } gboolean clone_unpack(resource_t *rsc, pe_working_set_t *data_set) { int lpc = 0; const char *type = NULL; resource_t *self = NULL; int num_xml_children = 0; xmlNode *xml_tmp = NULL; xmlNode *xml_self = NULL; xmlNode *xml_obj = rsc->xml; clone_variant_data_t *clone_data = NULL; const char *ordered = g_hash_table_lookup( rsc->meta, XML_RSC_ATTR_ORDERED); const char *interleave = g_hash_table_lookup( rsc->meta, XML_RSC_ATTR_INTERLEAVE); const char *max_clones = g_hash_table_lookup( rsc->meta, XML_RSC_ATTR_INCARNATION_MAX); const char *max_clones_node = g_hash_table_lookup( rsc->meta, XML_RSC_ATTR_INCARNATION_NODEMAX); crm_debug_3("Processing resource %s...", rsc->id); crm_malloc0(clone_data, sizeof(clone_variant_data_t)); rsc->variant_opaque = clone_data; clone_data->interleave = FALSE; clone_data->ordered = FALSE; clone_data->active_clones = 0; clone_data->xml_obj_child = NULL; clone_data->clone_node_max = crm_parse_int(max_clones_node, "1"); if(max_clones) { clone_data->clone_max = crm_parse_int(max_clones, "1"); } else if(g_list_length(data_set->nodes) > 0) { clone_data->clone_max = g_list_length(data_set->nodes); } else { clone_data->clone_max = 1; /* Handy during crm_verify */ } if(crm_is_true(interleave)) { clone_data->interleave = TRUE; } if(crm_is_true(ordered)) { clone_data->ordered = TRUE; } if((rsc->flags & pe_rsc_unique) == 0 && clone_data->clone_node_max > 1) { crm_config_err("Anonymous clones (%s) may only support one copy" " per node", rsc->id); clone_data->clone_node_max = 1; } crm_debug_2("Options for %s", rsc->id); crm_debug_2("\tClone max: %d", clone_data->clone_max); crm_debug_2("\tClone node max: %d", clone_data->clone_node_max); crm_debug_2("\tClone is unique: %s", is_set(rsc->flags, pe_rsc_unique)?"true":"false"); clone_data->xml_obj_child = find_xml_node( xml_obj, XML_CIB_TAG_GROUP, FALSE); if(clone_data->xml_obj_child == NULL) { clone_data->xml_obj_child = find_xml_node( xml_obj, XML_CIB_TAG_RESOURCE, TRUE); } else { xml_child_iter_filter(xml_obj, a_child, XML_CIB_TAG_RESOURCE, num_xml_children++); } if(clone_data->xml_obj_child == NULL) { crm_config_err("%s has nothing to clone", rsc->id); return FALSE; } type = crm_element_name(clone_data->xml_obj_child); xml_child_iter_filter(xml_obj, a_child, type, num_xml_children++); if(num_xml_children > 1) { crm_config_err("%s has too many children. Only the first (%s) will be cloned.", rsc->id, ID(clone_data->xml_obj_child)); } xml_self = copy_xml(rsc->xml); /* this is a bit of a hack - but simplifies everything else */ xmlNodeSetName(xml_self, ((const xmlChar*)XML_CIB_TAG_RESOURCE)); /* set_id(xml_self, "self", -1); */ xml_tmp = find_xml_node(xml_obj, "operations", FALSE); if(xml_tmp != NULL) { add_node_copy(xml_self, xml_tmp); } /* Make clones ever so slightly sticky by default * This is the only way to ensure clone instances are not * shuffled around the cluster for no benefit */ add_hash_param(rsc->meta, XML_RSC_ATTR_STICKINESS, "1"); if(common_unpack(xml_self, &self, rsc, data_set)) { clone_data->self = self; } else { crm_log_xml_err(xml_self, "Couldnt unpack dummy child"); clone_data->self = self; return FALSE; } crm_debug_2("\tClone is unique (fixed): %s", is_set(rsc->flags, pe_rsc_unique)?"true":"false"); clone_data->notify_confirm = is_set(rsc->flags, pe_rsc_notify); add_hash_param(rsc->meta, XML_RSC_ATTR_UNIQUE, is_set(rsc->flags, pe_rsc_unique)?XML_BOOLEAN_TRUE:XML_BOOLEAN_FALSE); for(lpc = 0; lpc < clone_data->clone_max; lpc++) { create_child_clone(rsc, lpc, data_set); } if(clone_data->clone_max == 0) { /* create one so that unpack_find_resource() will hook up * any orphans up to the parent correctly */ create_child_clone(rsc, -1, data_set); } crm_debug_3("Added %d children to resource %s...", clone_data->clone_max, rsc->id); return TRUE; } gboolean clone_active(resource_t *rsc, gboolean all) { clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); slist_iter( child_rsc, resource_t, rsc->children, lpc, gboolean child_active = child_rsc->fns->active(child_rsc, all); if(all == FALSE && child_active) { return TRUE; } else if(all && child_active == FALSE) { return FALSE; } ); if(all) { return TRUE; } else { return FALSE; } } static char * add_list_element(char *list, const char *value) { int len = 0; int last = 0; if(value == NULL) { return list; } if(list) { last = strlen(list); } len = last + 2; /* +1 space, +1 EOS */ len += strlen(value); crm_realloc(list, len); sprintf(list + last, " %s", value); return list; } static void short_print(char *list, const char *prefix, const char *type, long options, void *print_data) { if(list) { if(options & pe_print_html) { status_print("
  • "); } status_print("%s%s: [%s ]", prefix, type, list); if(options & pe_print_html) { status_print("
  • \n"); } else if(options & pe_print_suppres_nl) { /* nothing */ } else if((options & pe_print_printf) || (options & pe_print_ncurses)) { status_print("\n"); } } } void clone_print( resource_t *rsc, const char *pre_text, long options, void *print_data) { char *child_text = NULL; char *master_list = NULL; char *started_list = NULL; char *stopped_list = NULL; const char *type = "Clone"; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); if(pre_text == NULL) { pre_text = " "; } child_text = crm_concat(pre_text, " ", ' '); if(rsc->variant == pe_master) { type = "Master/Slave"; } status_print("%s%s Set: %s%s%s", pre_text?pre_text:"", type, rsc->id, is_set(rsc->flags, pe_rsc_unique)?" (unique)":"", is_set(rsc->flags, pe_rsc_managed)?"":" (unmanaged)"); if(options & pe_print_html) { status_print("\n
      \n"); } else if((options & pe_print_log) == 0) { status_print("\n"); } slist_iter( child_rsc, resource_t, rsc->children, lpc, gboolean print_full = FALSE; if(child_rsc->fns->active(child_rsc, FALSE) == FALSE) { /* Inactive clone */ if(is_set(child_rsc->flags, pe_rsc_orphan)) { continue; } else if(is_set(rsc->flags, pe_rsc_unique)) { print_full = TRUE; } else { stopped_list = add_list_element(stopped_list, child_rsc->id); } } else if(is_set(child_rsc->flags, pe_rsc_unique) || is_set(child_rsc->flags, pe_rsc_orphan) || is_set(child_rsc->flags, pe_rsc_managed) == FALSE || is_set(child_rsc->flags, pe_rsc_failed)) { /* Unique, unmanaged or failed clone */ print_full = TRUE; } else if(child_rsc->fns->active(child_rsc, TRUE)) { /* Fully active anonymous clone */ node_t *location = child_rsc->fns->location(child_rsc, NULL, TRUE); if(location) { const char *host = location->details->uname; enum rsc_role_e a_role = child_rsc->fns->state(child_rsc, TRUE); if(a_role > RSC_ROLE_SLAVE) { /* And active on a single node as master */ master_list = add_list_element(master_list, host); } else { /* And active on a single node as started/slave */ started_list = add_list_element(started_list, host); } } else { /* uncolocated group - bleh */ print_full = TRUE; } } else { /* Partially active anonymous clone */ print_full = TRUE; } if(print_full) { if(options & pe_print_html) { status_print("
    • \n"); } child_rsc->fns->print( child_rsc, child_text, options, print_data); if(options & pe_print_html) { status_print("
    • \n"); } } ); short_print(master_list, child_text, "Masters", options, print_data); short_print(started_list, child_text, rsc->variant==pe_master?"Slaves":"Started", options, print_data); short_print(stopped_list, child_text, "Stopped", options, print_data); crm_free(master_list); crm_free(started_list); crm_free(stopped_list); if(options & pe_print_html) { status_print("
    \n"); } crm_free(child_text); } void clone_free(resource_t *rsc) { clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); crm_debug_3("Freeing %s", rsc->id); slist_iter( child_rsc, resource_t, rsc->children, lpc, crm_debug_3("Freeing child %s", child_rsc->id); free_xml(child_rsc->xml); child_rsc->fns->free(child_rsc); ); crm_debug_3("Freeing child list"); pe_free_shallow_adv(rsc->children, FALSE); if(clone_data->self) { free_xml(clone_data->self->xml); clone_data->self->fns->free(clone_data->self); } + + CRM_ASSERT(clone_data->demote_notify == NULL); + CRM_ASSERT(clone_data->stop_notify == NULL); + CRM_ASSERT(clone_data->start_notify == NULL); + CRM_ASSERT(clone_data->promote_notify == NULL); + common_free(rsc); } enum rsc_role_e clone_resource_state(const resource_t *rsc, gboolean current) { enum rsc_role_e clone_role = RSC_ROLE_UNKNOWN; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); slist_iter( child_rsc, resource_t, rsc->children, lpc, enum rsc_role_e a_role = child_rsc->fns->state(child_rsc, current); if(a_role > clone_role) { clone_role = a_role; } ); crm_debug_3("%s role: %s", rsc->id, role2text(clone_role)); return clone_role; } diff --git a/pengine/allocate.c b/pengine/allocate.c index 5e68dc7a94..36fba1a7a2 100644 --- a/pengine/allocate.c +++ b/pengine/allocate.c @@ -1,1698 +1,1701 @@ /* * 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.1 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include void set_alloc_actions(pe_working_set_t *data_set); void migrate_reload_madness(pe_working_set_t *data_set); resource_alloc_functions_t resource_class_alloc_functions[] = { { native_merge_weights, native_color, native_create_actions, native_create_probe, native_internal_constraints, native_rsc_colocation_lh, native_rsc_colocation_rh, native_rsc_order_lh, native_rsc_order_rh, native_rsc_location, native_expand, complex_migrate_reload, complex_stonith_ordering, native_append_meta, }, { group_merge_weights, group_color, group_create_actions, native_create_probe, group_internal_constraints, group_rsc_colocation_lh, group_rsc_colocation_rh, group_rsc_order_lh, group_rsc_order_rh, group_rsc_location, group_expand, complex_migrate_reload, complex_stonith_ordering, group_append_meta, }, { native_merge_weights, clone_color, clone_create_actions, clone_create_probe, clone_internal_constraints, clone_rsc_colocation_lh, clone_rsc_colocation_rh, clone_rsc_order_lh, clone_rsc_order_rh, clone_rsc_location, clone_expand, complex_migrate_reload, complex_stonith_ordering, clone_append_meta, }, { native_merge_weights, master_color, master_create_actions, clone_create_probe, master_internal_constraints, clone_rsc_colocation_lh, master_rsc_colocation_rh, clone_rsc_order_lh, clone_rsc_order_rh, clone_rsc_location, clone_expand, complex_migrate_reload, complex_stonith_ordering, master_append_meta, } }; static gboolean check_rsc_parameters(resource_t *rsc, node_t *node, xmlNode *rsc_entry, pe_working_set_t *data_set) { int attr_lpc = 0; gboolean force_restart = FALSE; gboolean delete_resource = FALSE; const char *value = NULL; const char *old_value = NULL; const char *attr_list[] = { XML_ATTR_TYPE, XML_AGENT_ATTR_CLASS, XML_AGENT_ATTR_PROVIDER }; for(; attr_lpc < DIMOF(attr_list); attr_lpc++) { value = crm_element_value(rsc->xml, attr_list[attr_lpc]); old_value = crm_element_value(rsc_entry, attr_list[attr_lpc]); if(value == old_value /* ie. NULL */ || crm_str_eq(value, old_value, TRUE)) { continue; } force_restart = TRUE; crm_notice("Forcing restart of %s on %s, %s changed: %s -> %s", rsc->id, node->details->uname, attr_list[attr_lpc], crm_str(old_value), crm_str(value)); } if(force_restart) { /* make sure the restart happens */ stop_action(rsc, node, FALSE); set_bit(rsc->flags, pe_rsc_start_pending); delete_resource = TRUE; } return delete_resource; } static void CancelXmlOp(resource_t *rsc, xmlNode *xml_op, node_t *active_node, const char *reason, pe_working_set_t *data_set) { int interval = 0; action_t *cancel = NULL; char *key = NULL; const char *task = NULL; const char *call_id = NULL; const char *op_version = NULL; const char *interval_s = NULL; CRM_CHECK(xml_op != NULL, return); CRM_CHECK(active_node != NULL, return); task = crm_element_value(xml_op, XML_LRM_ATTR_TASK); call_id = crm_element_value(xml_op, XML_LRM_ATTR_CALLID); op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION); interval_s = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL); interval = crm_parse_int(interval_s, "0"); /* we need to reconstruct the key because of the way we used to construct resource IDs */ key = generate_op_key(rsc->id, task, interval); crm_info("Action %s on %s will be stopped: %s", key, active_node->details->uname, reason?reason:"unknown"); cancel = custom_action(rsc, crm_strdup(key), RSC_CANCEL, active_node, FALSE, TRUE, data_set); crm_free(cancel->task); cancel->task = crm_strdup(RSC_CANCEL); add_hash_param(cancel->meta, XML_LRM_ATTR_TASK, task); add_hash_param(cancel->meta, XML_LRM_ATTR_CALLID, call_id); add_hash_param(cancel->meta, XML_LRM_ATTR_INTERVAL, interval_s); custom_action_order(rsc, stop_key(rsc), NULL, rsc, NULL, cancel, pe_order_optional, data_set); crm_free(key); key = NULL; } static gboolean check_action_definition(resource_t *rsc, node_t *active_node, xmlNode *xml_op, pe_working_set_t *data_set) { char *key = NULL; int interval = 0; const char *interval_s = NULL; gboolean did_change = FALSE; gboolean start_op = FALSE; xmlNode *params_all = NULL; xmlNode *params_restart = NULL; GHashTable *local_rsc_params = NULL; char *digest_all_calc = NULL; const char *digest_all = NULL; const char *restart_list = NULL; const char *digest_restart = NULL; char *digest_restart_calc = NULL; action_t *action = NULL; const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK); const char *op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION); CRM_CHECK(active_node != NULL, return FALSE); interval_s = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL); interval = crm_parse_int(interval_s, "0"); /* we need to reconstruct the key because of the way we used to construct resource IDs */ key = generate_op_key(rsc->id, task, interval); if(interval > 0) { xmlNode *op_match = NULL; crm_debug_2("Checking parameters for %s", key); op_match = find_rsc_op_entry(rsc, key); if(op_match == NULL && is_set(data_set->flags, pe_flag_stop_action_orphans)) { CancelXmlOp(rsc, xml_op, active_node, "orphan", data_set); + crm_free(key); key = NULL; return TRUE; } else if(op_match == NULL) { crm_debug("Orphan action detected: %s on %s", key, active_node->details->uname); crm_free(key); key = NULL; return TRUE; } } action = custom_action(rsc, key, task, active_node, TRUE, FALSE, data_set); local_rsc_params = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); get_rsc_attributes(local_rsc_params, rsc, active_node, data_set); params_all = create_xml_node(NULL, XML_TAG_PARAMS); g_hash_table_foreach(local_rsc_params, hash2field, params_all); g_hash_table_foreach(action->extra, hash2field, params_all); g_hash_table_foreach(rsc->parameters, hash2field, params_all); g_hash_table_foreach(action->meta, hash2metafield, params_all); filter_action_parameters(params_all, op_version); digest_all_calc = calculate_xml_digest(params_all, TRUE, FALSE); digest_all = crm_element_value(xml_op, XML_LRM_ATTR_OP_DIGEST); digest_restart = crm_element_value(xml_op, XML_LRM_ATTR_RESTART_DIGEST); restart_list = crm_element_value(xml_op, XML_LRM_ATTR_OP_RESTART); if(crm_str_eq(task, RSC_START, TRUE)) { start_op = TRUE; } if(start_op && digest_restart) { params_restart = copy_xml(params_all); if(restart_list) { filter_reload_parameters(params_restart, restart_list); } digest_restart_calc = calculate_xml_digest(params_restart, TRUE, FALSE); if(safe_str_neq(digest_restart_calc, digest_restart)) { did_change = TRUE; crm_log_xml_info(params_restart, "params:restart"); crm_warn("Parameters to %s on %s changed: recorded %s vs. %s (restart:%s) %s", key, active_node->details->uname, crm_str(digest_restart), digest_restart_calc, op_version, crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC)); key = generate_op_key(rsc->id, task, interval); custom_action(rsc, key, task, NULL, FALSE, TRUE, data_set); goto cleanup; } } if(safe_str_neq(digest_all_calc, digest_all)) { action_t *op = NULL; did_change = TRUE; crm_log_xml_info(params_all, "params:all"); crm_warn("Parameters to %s on %s changed: recorded %s vs. %s (all:%s) %s", key, active_node->details->uname, crm_str(digest_all), digest_all_calc, op_version, crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC)); if(interval == 0 && safe_str_neq(task, RSC_STOP)) { /* Anything except stop actions should result in a restart, * never a re-probe */ task = RSC_START; } key = generate_op_key(rsc->id, task, interval); op = custom_action(rsc, key, task, NULL, FALSE, TRUE, data_set); if(start_op && digest_restart) { op->allow_reload_conversion = TRUE; } else if(interval > 0) { custom_action_order(rsc, start_key(rsc), NULL, NULL, crm_strdup(op->task), op, pe_order_runnable_left, data_set); } } cleanup: free_xml(params_all); free_xml(params_restart); crm_free(digest_all_calc); crm_free(digest_restart_calc); g_hash_table_destroy(local_rsc_params); pe_free_action(action); return did_change; } extern gboolean DeleteRsc(resource_t *rsc, node_t *node, gboolean optional, pe_working_set_t *data_set); static void check_actions_for(xmlNode *rsc_entry, resource_t *rsc, node_t *node, pe_working_set_t *data_set) { int interval = 0; int stop_index = 0; int start_index = 0; const char *id = NULL; const char *task = NULL; const char *interval_s = NULL; GListPtr op_list = NULL; GListPtr sorted_op_list = NULL; gboolean is_probe = FALSE; CRM_CHECK(node != NULL, return); if(is_set(rsc->flags, pe_rsc_orphan)) { crm_debug_2("Skipping param check for %s: orphan", rsc->id); return; } else if(pe_find_node_id(rsc->running_on, node->details->id) == NULL) { crm_debug_2("Skipping param check for %s: no longer active on %s", rsc->id, node->details->uname); return; } crm_debug_3("Processing %s on %s", rsc->id, node->details->uname); if(check_rsc_parameters(rsc, node, rsc_entry, data_set)) { DeleteRsc(rsc, node, FALSE, data_set); } xml_child_iter_filter( rsc_entry, rsc_op, XML_LRM_TAG_RSC_OP, op_list = g_list_append(op_list, rsc_op); ); sorted_op_list = g_list_sort(op_list, sort_op_by_callid); calculate_active_ops(sorted_op_list, &start_index, &stop_index); slist_iter( rsc_op, xmlNode, sorted_op_list, lpc, if(start_index < stop_index) { /* stopped */ continue; } else if(lpc < start_index) { /* action occurred prior to a start */ continue; } id = ID(rsc_op); is_probe = FALSE; task = crm_element_value(rsc_op, XML_LRM_ATTR_TASK); interval_s = crm_element_value(rsc_op, XML_LRM_ATTR_INTERVAL); interval = crm_parse_int(interval_s, "0"); if(interval == 0 && safe_str_eq(task, RSC_STATUS)) { is_probe = TRUE; } if(interval > 0 && is_set(data_set->flags, pe_flag_maintenance_mode)) { CancelXmlOp(rsc, rsc_op, node, "maintenance mode", data_set); } else if(is_probe || safe_str_eq(task, RSC_START) || interval > 0) { check_action_definition(rsc, node, rsc_op, data_set); } ); g_list_free(sorted_op_list); } static GListPtr find_rsc_list( GListPtr result, resource_t *rsc, const char *id, gboolean renamed_clones, gboolean partial, pe_working_set_t *data_set) { gboolean match = FALSE; if(id == NULL) { return NULL; } else if(rsc == NULL && data_set) { slist_iter(child, resource_t, data_set->resources, lpc, result = find_rsc_list(result, child, id, renamed_clones, partial, NULL)); return result; } if(partial) { if(strstr(rsc->id, id)) { match = TRUE; } else if(rsc->long_name && strstr(rsc->long_name, id)) { match = TRUE; } else if(renamed_clones && rsc->clone_name && strstr(rsc->clone_name, id)) { match = TRUE; } } else { if(strcmp(rsc->id, id) == 0){ match = TRUE; } else if(rsc->long_name && strcmp(rsc->long_name, id) == 0) { match = TRUE; } else if(renamed_clones && rsc->clone_name && strcmp(rsc->clone_name, id) == 0) { match = TRUE; } } if(match) { result = g_list_append(result, rsc); } if(rsc->children) { slist_iter(child, resource_t, rsc->children, lpc, result = find_rsc_list(result, child, id, renamed_clones, partial, NULL); ); } return result; } static void check_actions(pe_working_set_t *data_set) { const char *id = NULL; node_t *node = NULL; xmlNode *lrm_rscs = NULL; xmlNode *status = get_object_root(XML_CIB_TAG_STATUS, data_set->input); xml_child_iter_filter( status, node_state, XML_CIB_TAG_STATE, id = crm_element_value(node_state, XML_ATTR_ID); lrm_rscs = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE); lrm_rscs = find_xml_node(lrm_rscs, XML_LRM_TAG_RESOURCES, FALSE); node = pe_find_node_id(data_set->nodes, id); if(node == NULL) { continue; } else if(can_run_resources(node) == FALSE) { crm_debug_2("Skipping param check for %s: cant run resources", node->details->uname); continue; } crm_debug_2("Processing node %s", node->details->uname); if(node->details->online || is_set(data_set->flags, pe_flag_stonith_enabled)) { xml_child_iter_filter( lrm_rscs, rsc_entry, XML_LRM_TAG_RESOURCE, if(xml_has_children(rsc_entry)) { GListPtr result = NULL; const char *rsc_id = ID(rsc_entry); CRM_CHECK(rsc_id != NULL, return); result = find_rsc_list(NULL, NULL, rsc_id, TRUE, FALSE, data_set); slist_iter(rsc, resource_t, result, lpc, check_actions_for(rsc_entry, rsc, node, data_set)); g_list_free(result); } ); } ); } static gboolean apply_placement_constraints(pe_working_set_t *data_set) { crm_debug_3("Applying constraints..."); slist_iter( cons, rsc_to_node_t, data_set->placement_constraints, lpc, cons->rsc_lh->cmds->rsc_location(cons->rsc_lh, cons); ); return TRUE; } static void common_apply_stickiness(resource_t *rsc, node_t *node, pe_working_set_t *data_set) { int fail_count = 0; resource_t *failed = rsc; if(rsc->children) { slist_iter( child_rsc, resource_t, rsc->children, lpc, common_apply_stickiness(child_rsc, node, data_set); ); return; } if(is_set(rsc->flags, pe_rsc_managed) && rsc->stickiness != 0 && g_list_length(rsc->running_on) == 1) { node_t *current = pe_find_node_id(rsc->running_on, node->details->id); node_t *match = pe_find_node_id(rsc->allowed_nodes, node->details->id); if(current == NULL) { } else if(match != NULL || is_set(data_set->flags, pe_flag_symmetric_cluster)) { resource_t *sticky_rsc = rsc; resource_location(sticky_rsc, node, rsc->stickiness, "stickiness", data_set); crm_debug("Resource %s: preferring current location" " (node=%s, weight=%d)", sticky_rsc->id, node->details->uname, rsc->stickiness); } else { crm_debug("Ignoring stickiness for %s: the cluster is asymmetric" " and node %s is not explicitly allowed", rsc->id, node->details->uname); slist_iter(node, node_t, rsc->allowed_nodes, lpc, crm_err("%s[%s] = %d", rsc->id, node->details->uname, node->weight)); } } if(is_not_set(rsc->flags, pe_rsc_unique)) { failed = uber_parent(rsc); } fail_count = get_failcount(node, rsc, NULL, data_set); if(fail_count > 0 && rsc->migration_threshold != 0) { if(rsc->migration_threshold <= fail_count) { resource_location(failed, node, -INFINITY, "__fail_limit__", data_set); crm_warn("Forcing %s away from %s after %d failures (max=%d)", failed->id, node->details->uname, fail_count, rsc->migration_threshold); } else { crm_notice("%s can fail %d more times on %s before being forced off", failed->id, rsc->migration_threshold - fail_count, node->details->uname); } } } static void complex_set_cmds(resource_t *rsc) { rsc->cmds = &resource_class_alloc_functions[rsc->variant]; slist_iter( child_rsc, resource_t, rsc->children, lpc, complex_set_cmds(child_rsc); ); } void set_alloc_actions(pe_working_set_t *data_set) { slist_iter( rsc, resource_t, data_set->resources, lpc, complex_set_cmds(rsc); ); } static void calculate_system_health (gpointer gKey, gpointer gValue, gpointer user_data) { const char *key = (const char *)gKey; const char *value = (const char *)gValue; int *system_health = (int *)user_data; if (!gKey || !gValue || !user_data) { return; } /* Does it start with #health? */ if (0 == strncmp (key, "#health", 7)) { int score; /* Convert the value into an integer */ score = char2score (value); /* Add it to the running total */ *system_health = merge_weights (score, *system_health); } } static gboolean apply_system_health(pe_working_set_t *data_set) { const char *health_strategy = pe_pref(data_set->config_hash, "node-health-strategy"); if (health_strategy == NULL || safe_str_eq (health_strategy, "none")) { /* Prevent any accidental health -> score translation */ node_score_red = 0; node_score_yellow = 0; node_score_green = 0; return TRUE; } else if (safe_str_eq (health_strategy, "migrate-on-red")) { /* Resources on nodes which have health values of red are * weighted away from that node. */ node_score_red = -INFINITY; node_score_yellow = 0; node_score_green = 0; } else if (safe_str_eq (health_strategy, "only-green")) { /* Resources on nodes which have health values of red or yellow * are forced away from that node. */ node_score_red = -INFINITY; node_score_yellow = -INFINITY; node_score_green = 0; } else if (safe_str_eq (health_strategy, "progressive")) { /* Same as the above, but use the r/y/g scores provided by the user * Defaults are provided by the pe_prefs table */ } else if (safe_str_eq (health_strategy, "custom")) { /* Requires the admin to configure the rsc_location constaints for * processing the stored health scores */ /* TODO: Check for the existance of appropriate node health constraints */ return TRUE; } else { crm_err ("Unknown node health strategy: %s", health_strategy); return FALSE; } crm_info ("Applying automated node health strategy: %s", health_strategy); slist_iter( node, node_t, data_set->nodes, lpc, int system_health = 0; /* Search through the node hash table for system health entries. */ g_hash_table_foreach ( node->details->attrs, calculate_system_health, &system_health); crm_info (" Node %s has an combined system health of %d", node->details->uname, system_health); /* If the health is non-zero, then create a new rsc2node so that the * weight will be added later on. */ if (system_health != 0) { slist_iter( rsc, resource_t, data_set->resources, lpc, rsc2node_new (health_strategy, rsc, system_health, node, data_set); ); } ); return TRUE; } gboolean stage0(pe_working_set_t *data_set) { xmlNode * cib_constraints = get_object_root( XML_CIB_TAG_CONSTRAINTS, data_set->input); if(data_set->input == NULL) { return FALSE; } cluster_status(data_set); set_alloc_actions(data_set); apply_system_health(data_set); unpack_constraints(cib_constraints, data_set); return TRUE; } /* * Check nodes for resources started outside of the LRM */ gboolean probe_resources(pe_working_set_t *data_set) { action_t *probe_complete = NULL; action_t *probe_node_complete = NULL; slist_iter( node, node_t, data_set->nodes, lpc, gboolean force_probe = FALSE; const char *probed = g_hash_table_lookup( node->details->attrs, CRM_OP_PROBED); if(node->details->online == FALSE) { continue; } else if(node->details->unclean) { continue; } else if(probe_complete == NULL) { probe_complete = get_pseudo_op(CRM_OP_PROBED, data_set); } if(probed != NULL && crm_is_true(probed) == FALSE) { force_probe = TRUE; } probe_node_complete = custom_action( NULL, crm_strdup(CRM_OP_PROBED), CRM_OP_PROBED, node, FALSE, TRUE, data_set); probe_node_complete->optional = crm_is_true(probed); probe_node_complete->priority = INFINITY; add_hash_param(probe_node_complete->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE); if(node->details->pending) { probe_node_complete->runnable = FALSE; crm_info("Action %s on %s is unrunnable (pending)", probe_node_complete->uuid, probe_node_complete->node->details->uname); } order_actions(probe_node_complete, probe_complete, pe_order_runnable_left); slist_iter( rsc, resource_t, data_set->resources, lpc2, if(rsc->cmds->create_probe( rsc, node, probe_node_complete, force_probe, data_set)) { probe_complete->optional = FALSE; probe_node_complete->optional = FALSE; custom_action_order( NULL, NULL, probe_complete, rsc, start_key(rsc), NULL, pe_order_optional, data_set); custom_action_order( NULL, NULL, probe_complete, rsc, stop_key(rsc), NULL, pe_order_optional, data_set); } ); ); return TRUE; } /* * Count how many valid nodes we have (so we know the maximum number of * colors we can resolve). * * Apply node constraints (ie. filter the "allowed_nodes" part of resources */ gboolean stage2(pe_working_set_t *data_set) { crm_debug_3("Applying placement constraints"); slist_iter( node, node_t, data_set->nodes, lpc, if(node == NULL) { /* error */ } else if(node->weight >= 0.0 /* global weight */ && node->details->online && node->details->type == node_member) { data_set->max_valid_nodes++; } ); apply_placement_constraints(data_set); slist_iter(node, node_t, data_set->nodes, lpc, slist_iter( rsc, resource_t, data_set->resources, lpc2, common_apply_stickiness(rsc, node, data_set); ); ); return TRUE; } /* * Create internal resource constraints before allocation */ gboolean stage3(pe_working_set_t *data_set) { slist_iter( rsc, resource_t, data_set->resources, lpc, rsc->cmds->internal_constraints(rsc, data_set); ); return TRUE; } /* * Check for orphaned or redefined actions */ gboolean stage4(pe_working_set_t *data_set) { check_actions(data_set); return TRUE; } gboolean stage5(pe_working_set_t *data_set) { slist_iter( node, node_t, data_set->nodes, lpc, dump_node_capacity(show_utilization?0:utilization_log_level, "Original", node); ); /* Take (next) highest resource, assign it and create its actions */ slist_iter( rsc, resource_t, data_set->resources, lpc, rsc->cmds->color(rsc, data_set); ); slist_iter( node, node_t, data_set->nodes, lpc, dump_node_capacity(show_utilization?0:utilization_log_level, "Remaining", node); ); probe_resources(data_set); slist_iter( rsc, resource_t, data_set->resources, lpc, rsc->cmds->create_actions(rsc, data_set); ); return TRUE; } static gboolean is_managed(const resource_t *rsc) { if(is_set(rsc->flags, pe_rsc_managed)) { return TRUE; } slist_iter( child_rsc, resource_t, rsc->children, lpc, if(is_managed(child_rsc)) { return TRUE; } ); return FALSE; } static gboolean any_managed_resouces(pe_working_set_t *data_set) { slist_iter( rsc, resource_t, data_set->resources, lpc, if(is_managed(rsc)) { return TRUE; } ); return FALSE; } /* * Create dependancies for stonith and shutdown operations */ gboolean stage6(pe_working_set_t *data_set) { action_t *dc_down = NULL; action_t *stonith_op = NULL; action_t *last_stonith = NULL; gboolean integrity_lost = FALSE; action_t *ready = get_pseudo_op(STONITH_UP, data_set); action_t *all_stopped = get_pseudo_op(ALL_STOPPED, data_set); action_t *done = get_pseudo_op(STONITH_DONE, data_set); gboolean need_stonith = FALSE; crm_debug_3("Processing fencing and shutdown cases"); if(is_set(data_set->flags, pe_flag_stonith_enabled) && (is_set(data_set->flags, pe_flag_have_quorum) || data_set->no_quorum_policy == no_quorum_ignore || data_set->no_quorum_policy == no_quorum_suicide)) { need_stonith = TRUE; } if(need_stonith && any_managed_resouces(data_set) == FALSE) { crm_info("Delaying fencing operations until there are resources to manage"); need_stonith = FALSE; } slist_iter( node, node_t, data_set->nodes, lpc, stonith_op = NULL; if(node->details->unclean && need_stonith) { pe_warn("Scheduling Node %s for STONITH", node->details->uname); stonith_op = custom_action( NULL, crm_strdup(CRM_OP_FENCE), CRM_OP_FENCE, node, FALSE, TRUE, data_set); add_hash_param( stonith_op->meta, XML_LRM_ATTR_TARGET, node->details->uname); add_hash_param( stonith_op->meta, XML_LRM_ATTR_TARGET_UUID, node->details->id); add_hash_param( stonith_op->meta, "stonith_action", data_set->stonith_action); stonith_constraints(node, stonith_op, data_set); order_actions(ready, stonith_op, pe_order_implies_left); order_actions(stonith_op, all_stopped, pe_order_implies_right); if(node->details->is_dc) { dc_down = stonith_op; } else { if(last_stonith) { order_actions(last_stonith, stonith_op, pe_order_implies_left); } last_stonith = stonith_op; } } else if(node->details->online && node->details->shutdown) { action_t *down_op = NULL; crm_info("Scheduling Node %s for shutdown", node->details->uname); down_op = custom_action( NULL, crm_strdup(CRM_OP_SHUTDOWN), CRM_OP_SHUTDOWN, node, FALSE, TRUE, data_set); shutdown_constraints(node, down_op, data_set); add_hash_param(down_op->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE); if(node->details->is_dc) { dc_down = down_op; } } if(node->details->unclean && stonith_op == NULL) { integrity_lost = TRUE; pe_warn("Node %s is unclean!", node->details->uname); } ); if(integrity_lost) { if(is_set(data_set->flags, pe_flag_stonith_enabled) == FALSE) { pe_warn("YOUR RESOURCES ARE NOW LIKELY COMPROMISED"); pe_err("ENABLE STONITH TO KEEP YOUR RESOURCES SAFE"); } else if(is_set(data_set->flags, pe_flag_have_quorum) == FALSE) { crm_notice("Cannot fence unclean nodes until quorum is" " attained (or no-quorum-policy is set to ignore)"); } } if(dc_down != NULL) { GListPtr shutdown_matches = find_actions( data_set->actions, CRM_OP_SHUTDOWN, NULL); crm_debug_2("Ordering shutdowns before %s on %s (DC)", dc_down->task, dc_down->node->details->uname); add_hash_param(dc_down->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE); slist_iter( node_stop, action_t, shutdown_matches, lpc, if(node_stop->node->details->is_dc) { continue; } crm_debug("Ordering shutdown on %s before %s on %s", node_stop->node->details->uname, dc_down->task, dc_down->node->details->uname); order_actions(node_stop, dc_down, pe_order_implies_left); ); if(last_stonith && dc_down != last_stonith) { order_actions(last_stonith, dc_down, pe_order_implies_left); } g_list_free(shutdown_matches); } if(last_stonith) { order_actions(last_stonith, done, pe_order_implies_right); } order_actions(ready, done, pe_order_optional); return TRUE; } /* * Determin the sets of independant actions and the correct order for the * actions in each set. * * Mark dependencies of un-runnable actions un-runnable * */ gboolean stage7(pe_working_set_t *data_set) { crm_debug_4("Applying ordering constraints"); slist_iter( order, order_constraint_t, data_set->ordering_constraints, lpc, resource_t *rsc = order->lh_rsc; crm_debug_3("Applying ordering constraint: %d", order->id); if(rsc != NULL) { crm_debug_4("rsc_action-to-*"); rsc->cmds->rsc_order_lh(rsc, order, data_set); continue; } rsc = order->rh_rsc; if(rsc != NULL) { crm_debug_4("action-to-rsc_action"); rsc->cmds->rsc_order_rh(order->lh_action, rsc, order); } else { crm_debug_4("action-to-action"); order_actions( order->lh_action, order->rh_action, order->type); } ); /* This code may need optimization: * * ptest -x with 800 resources, 80 nodes, calling update_action_states() with: * no probes O(8s) * full probe detection but no creation O(2m) * full probe detection and creation O(Lifetime of the universe) * * Duplicate detection in order_actions() has resolved the issue for now, * the full detection with "creation" and "no creation" cases are now identical */ update_action_states(data_set->actions); slist_iter( rsc, resource_t, data_set->resources, lpc, rsc->cmds->migrate_reload(rsc, data_set); LogActions(rsc, data_set); ); return TRUE; } static gint sort_notify_entries(gconstpointer a, gconstpointer b) { int tmp; const notify_entry_t *entry_a = a; const notify_entry_t *entry_b = b; if(entry_a == NULL && entry_b == NULL) { return 0; } if(entry_a == NULL) { return 1; } if(entry_b == NULL) { return -1; } if(entry_a->rsc == NULL && entry_b->rsc == NULL) { return 0; } if(entry_a->rsc == NULL) { return 1; } if(entry_b->rsc == NULL) { return -1; } tmp = strcmp(entry_a->rsc->id, entry_b->rsc->id); if(tmp != 0) { return tmp; } if(entry_a->node == NULL && entry_b->node == NULL) { return 0; } if(entry_a->node == NULL) { return 1; } if(entry_b->node == NULL) { return -1; } return strcmp(entry_a->node->details->id, entry_b->node->details->id); } static void expand_list(GListPtr list, char **rsc_list, char **node_list) { const char *uname = NULL; const char *rsc_id = NULL; const char *last_rsc_id = NULL; if(list == NULL) { *rsc_list = crm_strdup(" "); if(node_list) { *node_list = crm_strdup(" "); } return; } *rsc_list = NULL; if(node_list) { *node_list = NULL; } slist_iter(entry, notify_entry_t, list, lpc, CRM_CHECK(entry != NULL, continue); CRM_CHECK(entry->rsc != NULL, continue); CRM_CHECK(node_list == NULL || entry->node != NULL, continue); uname = NULL; rsc_id = entry->rsc->id; CRM_ASSERT(rsc_id != NULL); /* filter dups */ if(safe_str_eq(rsc_id, last_rsc_id)) { continue; } last_rsc_id = rsc_id; if(rsc_list != NULL) { int existing_len = 0; int len = 2 + strlen(rsc_id); /* +1 space, +1 EOS */ if(rsc_list && *rsc_list) { existing_len = strlen(*rsc_list); } crm_debug_5("Adding %s (%dc) at offset %d", rsc_id, len-2, existing_len); crm_realloc(*rsc_list, len + existing_len); sprintf(*rsc_list + existing_len, "%s ", rsc_id); } if(entry->node != NULL) { uname = entry->node->details->uname; } if(node_list != NULL && uname) { int existing_len = 0; int len = 2 + strlen(uname); if(node_list && *node_list) { existing_len = strlen(*node_list); } crm_debug_5("Adding %s (%dc) at offset %d", uname, len-2, existing_len); crm_realloc(*node_list, len + existing_len); sprintf(*node_list + existing_len, "%s ", uname); } ); } static void dup_attr(gpointer key, gpointer value, gpointer user_data) { g_hash_table_replace(user_data, crm_strdup(key), crm_strdup(value)); } static action_t * pe_notify(resource_t *rsc, node_t *node, action_t *op, action_t *confirm, notify_data_t *n_data, pe_working_set_t *data_set) { char *key = NULL; action_t *trigger = NULL; const char *value = NULL; const char *task = NULL; if(op == NULL || confirm == NULL) { crm_debug_2("Op=%p confirm=%p", op, confirm); return NULL; } CRM_CHECK(node != NULL, return NULL); if(node->details->online == FALSE) { crm_debug_2("Skipping notification for %s: node offline", rsc->id); return NULL; } else if(op->runnable == FALSE) { crm_debug_2("Skipping notification for %s: not runnable", op->uuid); return NULL; } value = g_hash_table_lookup(op->meta, "notify_type"); task = g_hash_table_lookup(op->meta, "notify_operation"); crm_debug_2("Creating notify actions for %s: %s (%s-%s)", op->uuid, rsc->id, value, task); key = generate_notify_key(rsc->id, value, task); trigger = custom_action(rsc, key, op->task, node, op->optional, TRUE, data_set); g_hash_table_foreach(op->meta, dup_attr, trigger->meta); g_hash_table_foreach(n_data->keys, dup_attr, trigger->meta); /* pseudo_notify before notify */ crm_debug_3("Ordering %s before %s (%d->%d)", op->uuid, trigger->uuid, trigger->id, op->id); order_actions(op, trigger, pe_order_implies_left); order_actions(trigger, confirm, pe_order_implies_left); return trigger; } static void pe_post_notify(resource_t *rsc, node_t *node, notify_data_t *n_data, pe_working_set_t *data_set) { action_t *notify = NULL; CRM_CHECK(rsc != NULL, return); if(n_data->post == NULL) { return; /* Nothing to do */ } notify = pe_notify(rsc, node, n_data->post, n_data->post_done, n_data, data_set); if(notify != NULL) { notify->priority = INFINITY; } if(n_data->post_done) { slist_iter( mon, action_t, rsc->actions, lpc, const char *interval = g_hash_table_lookup(mon->meta, "interval"); if(interval == NULL || safe_str_eq(interval, "0")) { crm_debug_3("Skipping %s: interval", mon->uuid); continue; } else if(safe_str_eq(mon->task, "cancel")) { crm_debug_3("Skipping %s: cancel", mon->uuid); continue; } order_actions(n_data->post_done, mon, pe_order_optional); ); } } notify_data_t * create_notification_boundaries( resource_t *rsc, const char *action, action_t *start, action_t *end, pe_working_set_t *data_set) { /* Create the pseudo ops that preceed and follow the actual notifications */ /* * Creates two sequences (conditional on start and end being supplied): * pre_notify -> pre_notify_complete -> start, and * end -> post_notify -> post_notify_complete * * 'start' and 'end' may be the same event or ${X} and ${X}ed as per clones */ char *key = NULL; notify_data_t *n_data = NULL; if(is_not_set(rsc->flags, pe_rsc_notify)) { return NULL; } + crm_err("Creating boundaries for %s", rsc->id); + crm_malloc0(n_data, sizeof(notify_data_t)); n_data->action = action; n_data->keys = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); if(start) { /* create pre-event notification wrappers */ key = generate_notify_key(rsc->id, "pre", start->task); n_data->pre = custom_action( rsc, key, RSC_NOTIFY, NULL, start->optional, TRUE, data_set); n_data->pre->pseudo = TRUE; n_data->pre->runnable = TRUE; add_hash_param(n_data->pre->meta, "notify_type", "pre"); add_hash_param(n_data->pre->meta, "notify_operation", n_data->action); /* create pre_notify_complete */ key = generate_notify_key(rsc->id, "confirmed-pre", start->task); n_data->pre_done = custom_action( rsc, key, RSC_NOTIFIED, NULL, start->optional, TRUE, data_set); n_data->pre_done->pseudo = TRUE; n_data->pre_done->runnable = TRUE; add_hash_param(n_data->pre_done->meta, "notify_type", "pre"); add_hash_param(n_data->pre_done->meta, "notify_operation", n_data->action); order_actions(n_data->pre_done, start, pe_order_optional); order_actions(n_data->pre, n_data->pre_done, pe_order_optional); } if(end) { /* create post-event notification wrappers */ key = generate_notify_key(rsc->id, "post", end->task); n_data->post = custom_action( rsc, key, RSC_NOTIFY, NULL, end->optional, TRUE, data_set); n_data->post->pseudo = TRUE; n_data->post->runnable = TRUE; n_data->post->priority = INFINITY; n_data->post->runnable = end->runnable; add_hash_param(n_data->post->meta, "notify_type", "post"); add_hash_param(n_data->post->meta, "notify_operation", n_data->action); /* create post_notify_complete */ key = generate_notify_key(rsc->id, "confirmed-post", end->task); n_data->post_done = custom_action( rsc, key, RSC_NOTIFIED, NULL, end->optional, TRUE, data_set); n_data->post_done->pseudo = TRUE; n_data->post_done->runnable = TRUE; n_data->post_done->priority = INFINITY; n_data->post_done->runnable = end->runnable; add_hash_param(n_data->post_done->meta, "notify_type", "pre"); add_hash_param(n_data->post_done->meta, "notify_operation", n_data->action); order_actions(end, n_data->post, pe_order_implies_right); order_actions(n_data->post, n_data->post_done, pe_order_implies_right); } if(start && end) { order_actions(n_data->pre_done, n_data->post, pe_order_optional); } if(safe_str_eq(action, RSC_STOP)) { action_t *all_stopped = get_pseudo_op(ALL_STOPPED, data_set); order_actions(n_data->post_done, all_stopped, pe_order_optional); } return n_data; } void collect_notification_data(resource_t *rsc, gboolean state, gboolean activity, notify_data_t *n_data) { if(rsc->children) { slist_iter(child, resource_t, rsc->children, lpc, collect_notification_data(child, state, activity, n_data); ); return; } if(state) { notify_entry_t *entry = NULL; crm_malloc0(entry, sizeof(notify_entry_t)); entry->rsc = rsc; if(rsc->running_on) { /* we only take the first one */ entry->node = rsc->running_on->data; } crm_debug_2("%s state: %s", rsc->id, role2text(rsc->role)); switch(rsc->role) { case RSC_ROLE_STOPPED: n_data->inactive = g_list_append(n_data->inactive, entry); break; case RSC_ROLE_STARTED: n_data->active = g_list_append(n_data->active, entry); break; case RSC_ROLE_SLAVE: n_data->slave = g_list_append(n_data->slave, entry); break; case RSC_ROLE_MASTER: n_data->master = g_list_append(n_data->master, entry); break; default: crm_err("Unsupported notify role"); crm_free(entry); break; } } if(activity) { notify_entry_t *entry = NULL; enum action_tasks task; slist_iter( op, action_t, rsc->actions, lpc, if(op->optional == FALSE && op->node != NULL) { crm_malloc0(entry, sizeof(notify_entry_t)); entry->node = op->node; entry->rsc = rsc; task = text2task(op->task); switch(task) { case start_rsc: n_data->start = g_list_append(n_data->start, entry); break; case stop_rsc: n_data->stop = g_list_append(n_data->stop, entry); break; case action_promote: n_data->promote = g_list_append(n_data->promote, entry); break; case action_demote: n_data->demote = g_list_append(n_data->demote, entry); break; default: crm_free(entry); break; } } ); } } gboolean expand_notification_data(notify_data_t *n_data) { /* Expand the notification entries into a key=value hashtable * This hashtable is later used in action2xml() */ gboolean required = FALSE; char *rsc_list = NULL; char *node_list = NULL; if(n_data->stop) { n_data->stop = g_list_sort(n_data->stop, sort_notify_entries); } expand_list(n_data->stop, &rsc_list, &node_list); if(rsc_list != NULL && safe_str_neq(" ", rsc_list)) { if(safe_str_eq(n_data->action, RSC_STOP)) { required = TRUE; } } g_hash_table_insert(n_data->keys, crm_strdup("notify_stop_resource"), rsc_list); g_hash_table_insert(n_data->keys, crm_strdup("notify_stop_uname"), node_list); if(n_data->start) { n_data->start = g_list_sort(n_data->start, sort_notify_entries); if(rsc_list && safe_str_eq(n_data->action, RSC_START)) { required = TRUE; } } expand_list(n_data->start, &rsc_list, &node_list); g_hash_table_insert(n_data->keys, crm_strdup("notify_start_resource"), rsc_list); g_hash_table_insert(n_data->keys, crm_strdup("notify_start_uname"), node_list); if(n_data->demote) { n_data->demote = g_list_sort(n_data->demote, sort_notify_entries); if(safe_str_eq(n_data->action, RSC_DEMOTE)) { required = TRUE; } } expand_list(n_data->demote, &rsc_list, &node_list); g_hash_table_insert(n_data->keys, crm_strdup("notify_demote_resource"), rsc_list); g_hash_table_insert(n_data->keys, crm_strdup("notify_demote_uname"), node_list); if(n_data->promote) { n_data->promote = g_list_sort(n_data->promote, sort_notify_entries); if(safe_str_eq(n_data->action, RSC_PROMOTE)) { required = TRUE; } } expand_list(n_data->promote, &rsc_list, &node_list); g_hash_table_insert(n_data->keys, crm_strdup("notify_promote_resource"), rsc_list); g_hash_table_insert(n_data->keys, crm_strdup("notify_promote_uname"), node_list); if(n_data->active) { n_data->active = g_list_sort(n_data->active, sort_notify_entries); } expand_list(n_data->active, &rsc_list, &node_list); g_hash_table_insert(n_data->keys, crm_strdup("notify_active_resource"), rsc_list); g_hash_table_insert(n_data->keys, crm_strdup("notify_active_uname"), node_list); if(n_data->slave) { n_data->slave = g_list_sort(n_data->slave, sort_notify_entries); } expand_list(n_data->slave, &rsc_list, &node_list); g_hash_table_insert(n_data->keys, crm_strdup("notify_slave_resource"), rsc_list); g_hash_table_insert(n_data->keys, crm_strdup("notify_slave_uname"), node_list); if(n_data->master) { n_data->master = g_list_sort(n_data->master, sort_notify_entries); } expand_list(n_data->master, &rsc_list, &node_list); g_hash_table_insert(n_data->keys, crm_strdup("notify_master_resource"), rsc_list); g_hash_table_insert(n_data->keys, crm_strdup("notify_master_uname"), node_list); if(n_data->inactive) { n_data->inactive = g_list_sort(n_data->inactive, sort_notify_entries); } expand_list(n_data->inactive, &rsc_list, NULL); g_hash_table_insert(n_data->keys, crm_strdup("notify_inactive_resource"), rsc_list); if(required && n_data->pre) { n_data->pre->optional = FALSE; n_data->pre_done->optional = FALSE; } if(required && n_data->post) { n_data->post->optional = FALSE; n_data->post_done->optional = FALSE; } return required; } void create_notifications(resource_t *rsc, notify_data_t *n_data, pe_working_set_t *data_set) { action_t *stop = NULL; action_t *start = NULL; enum action_tasks task = text2task(n_data->action); if(rsc->children) { slist_iter( child, resource_t, rsc->children, lpc, create_notifications(child, n_data, data_set); ); return; } /* Copy notification details into standard ops */ slist_iter( op, action_t, rsc->actions, lpc, if(op->optional == FALSE && op->node != NULL) { enum action_tasks t = text2task(op->task); switch(t) { case start_rsc: case stop_rsc: case action_promote: case action_demote: g_hash_table_foreach(n_data->keys, dup_attr, op->meta); break; default: break; } } ); crm_debug_2("Creating notificaitons for: %s.%s (%s->%s)", n_data->action, rsc->id, role2text(rsc->role), role2text(rsc->next_role)); stop = find_first_action(rsc->actions, NULL, RSC_STOP, NULL); start = find_first_action(rsc->actions, NULL, RSC_START, NULL); /* stop / demote */ if(rsc->role != RSC_ROLE_STOPPED) { if(task == stop_rsc || task == action_demote) { slist_iter(current_node, node_t, rsc->running_on, lpc, pe_notify(rsc, current_node, n_data->pre, n_data->pre_done, n_data, data_set); if(task == action_demote || stop == NULL || stop->optional) { pe_post_notify(rsc, current_node, n_data, data_set); } ); } } /* start / promote */ if(rsc->next_role != RSC_ROLE_STOPPED) { if(rsc->allocated_to == NULL) { pe_proc_err("Next role '%s' but %s is not allocated", role2text(rsc->next_role), rsc->id); } else if(task == start_rsc || task == action_promote) { if(task != start_rsc || start == NULL || start->optional) { pe_notify(rsc, rsc->allocated_to, n_data->pre, n_data->pre_done, n_data, data_set); } pe_post_notify(rsc, rsc->allocated_to, n_data, data_set); } } } void free_notification_data(notify_data_t *n_data) { if(n_data == NULL) { return; } pe_free_shallow(n_data->stop); pe_free_shallow(n_data->start); pe_free_shallow(n_data->demote); pe_free_shallow(n_data->promote); pe_free_shallow(n_data->master); pe_free_shallow(n_data->slave); pe_free_shallow(n_data->active); pe_free_shallow(n_data->inactive); g_hash_table_destroy(n_data->keys); crm_free(n_data); } int transition_id = -1; /* * Create a dependency graph to send to the transitioner (via the CRMd) */ gboolean stage8(pe_working_set_t *data_set) { const char *value = NULL; transition_id++; crm_debug_2("Creating transition graph %d.", transition_id); data_set->graph = create_xml_node(NULL, XML_TAG_GRAPH); value = pe_pref(data_set->config_hash, "cluster-delay"); crm_xml_add(data_set->graph, "cluster-delay", value); value = pe_pref(data_set->config_hash, "stonith-timeout"); crm_xml_add(data_set->graph, "stonith-timeout", value); crm_xml_add(data_set->graph, "failed-stop-offset", "INFINITY"); if(is_set(data_set->flags, pe_flag_start_failure_fatal)) { crm_xml_add(data_set->graph, "failed-start-offset", "INFINITY"); } else { crm_xml_add(data_set->graph, "failed-start-offset", "1"); } value = pe_pref(data_set->config_hash, "batch-limit"); crm_xml_add(data_set->graph, "batch-limit", value); crm_xml_add_int(data_set->graph, "transition_id", transition_id); /* errors... slist_iter(action, action_t, action_list, lpc, if(action->optional == FALSE && action->runnable == FALSE) { print_action("Ignoring", action, TRUE); } ); */ slist_iter( rsc, resource_t, data_set->resources, lpc, crm_debug_4("processing actions for rsc=%s", rsc->id); rsc->cmds->expand(rsc, data_set); ); crm_log_xml_debug_3( data_set->graph, "created resource-driven action list"); /* catch any non-resource specific actions */ crm_debug_4("processing non-resource actions"); slist_iter( action, action_t, data_set->actions, lpc, graph_element_from_action(action, data_set); ); crm_log_xml_debug_3(data_set->graph, "created generic action list"); crm_debug_2("Created transition graph %d.", transition_id); return TRUE; } void cleanup_alloc_calculations(pe_working_set_t *data_set) { if(data_set == NULL) { return; } crm_debug_3("deleting order cons: %p", data_set->ordering_constraints); pe_free_ordering(data_set->ordering_constraints); data_set->ordering_constraints = NULL; crm_debug_3("deleting node cons: %p", data_set->placement_constraints); pe_free_rsc_to_node(data_set->placement_constraints); data_set->placement_constraints = NULL; crm_debug_3("deleting inter-resource cons: %p", data_set->colocation_constraints); pe_free_shallow(data_set->colocation_constraints); data_set->colocation_constraints = NULL; cleanup_calculations(data_set); } diff --git a/pengine/clone.c b/pengine/clone.c index a33fe9ec3c..81263dbbcd 100644 --- a/pengine/clone.c +++ b/pengine/clone.c @@ -1,1731 +1,1737 @@ /* * 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.1 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #define VARIANT_CLONE 1 #include gint sort_clone_instance(gconstpointer a, gconstpointer b, gpointer data_set); void child_stopping_constraints( clone_variant_data_t *clone_data, resource_t *self, resource_t *child, resource_t *last, pe_working_set_t *data_set); void child_starting_constraints( clone_variant_data_t *clone_data, resource_t *self, resource_t *child, resource_t *last, pe_working_set_t *data_set); static node_t * parent_node_instance(const resource_t *rsc, node_t *node) { node_t *ret = NULL; if(node != NULL) { ret = pe_find_node_id( rsc->parent->allowed_nodes, node->details->id); } return ret; } static gboolean did_fail(const resource_t *rsc) { if(is_set(rsc->flags, pe_rsc_failed)) { return TRUE; } slist_iter( child_rsc, resource_t, rsc->children, lpc, if(did_fail(child_rsc)) { return TRUE; } ); return FALSE; } gint sort_clone_instance(gconstpointer a, gconstpointer b, gpointer data_set) { int level = LOG_DEBUG_3; node_t *node1 = NULL; node_t *node2 = NULL; gboolean can1 = TRUE; gboolean can2 = TRUE; gboolean with_scores = 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 cant support them or are to be fenced * - failed instances * - inactive instances */ do_crm_log_unlikely(level+1, "%s ? %s", resource1->id, resource2->id); if(resource1->running_on && resource2->running_on) { if(g_list_length(resource1->running_on) < g_list_length(resource2->running_on)) { do_crm_log_unlikely(level, "%s < %s: running_on", resource1->id, resource2->id); return -1; } else if(g_list_length(resource1->running_on) > g_list_length(resource2->running_on)) { do_crm_log_unlikely(level, "%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_find_node_id(resource1->allowed_nodes, node1->details->id); if(match == NULL || match->weight < 0) { do_crm_log_unlikely(level, "%s: current location is unavailable", resource1->id); node1 = NULL; can1 = FALSE; } } if(node2) { node_t *match = pe_find_node_id(resource2->allowed_nodes, node2->details->id); if(match == NULL || match->weight < 0) { do_crm_log_unlikely(level, "%s: current location is unavailable", resource2->id); node2 = NULL; can2 = FALSE; } } if(can1 != can2) { if(can1) { do_crm_log_unlikely(level, "%s < %s: availability of current location", resource1->id, resource2->id); return -1; } do_crm_log_unlikely(level, "%s > %s: availability of current location", resource1->id, resource2->id); return 1; } if(resource1->priority < resource2->priority) { do_crm_log_unlikely(level, "%s < %s: priority", resource1->id, resource2->id); return 1; } else if(resource1->priority > resource2->priority) { do_crm_log_unlikely(level, "%s > %s: priority", resource1->id, resource2->id); return -1; } if(node1 == NULL && node2 == NULL) { do_crm_log_unlikely(level, "%s == %s: not active", resource1->id, resource2->id); return 0; } if(node1 != node2) { if(node1 == NULL) { do_crm_log_unlikely(level, "%s > %s: active", resource1->id, resource2->id); return 1; } else if(node2 == NULL) { do_crm_log_unlikely(level, "%s < %s: active", resource1->id, resource2->id); return -1; } } can1 = can_run_resources(node1); can2 = can_run_resources(node2); if(can1 != can2) { if(can1) { do_crm_log_unlikely(level, "%s < %s: can", resource1->id, resource2->id); return -1; } do_crm_log_unlikely(level, "%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) { do_crm_log_unlikely(level, "%s < %s: not allowed", resource1->id, resource2->id); return -1; } else if(node1 == NULL && node2 != NULL) { do_crm_log_unlikely(level, "%s > %s: not allowed", resource1->id, resource2->id); return 1; } if(node1 == NULL) { do_crm_log_unlikely(level, "%s == %s: not allowed", resource1->id, resource2->id); return 0; } if(node1->count < node2->count) { do_crm_log_unlikely(level, "%s < %s: count", resource1->id, resource2->id); return -1; } else if(node1->count > node2->count) { do_crm_log_unlikely(level, "%s > %s: count", resource1->id, resource2->id); return 1; } if(with_scores) { int max = 0; int lpc = 0; GListPtr list1 = node_list_dup(resource1->allowed_nodes, FALSE, FALSE); GListPtr list2 = node_list_dup(resource2->allowed_nodes, FALSE, FALSE); list1 = g_list_sort_with_data(list1, sort_node_weight, data_set); list2 = g_list_sort_with_data(list2, sort_node_weight, data_set); 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) { do_crm_log_unlikely(level, "%s < %s: node score NULL", resource1->id, resource2->id); pe_free_shallow(list1); pe_free_shallow(list2); return 1; } else if(node2 == NULL) { do_crm_log_unlikely(level, "%s > %s: node score NULL", resource1->id, resource2->id); pe_free_shallow(list1); pe_free_shallow(list2); return -1; } if(node1->weight < node2->weight) { do_crm_log_unlikely(level, "%s < %s: node score", resource1->id, resource2->id); pe_free_shallow(list1); pe_free_shallow(list2); return 1; } else if(node1->weight > node2->weight) { do_crm_log_unlikely(level, "%s > %s: node score", resource1->id, resource2->id); pe_free_shallow(list1); pe_free_shallow(list2); return -1; } } pe_free_shallow(list1); pe_free_shallow(list2); } can1 = did_fail(resource1); can2 = did_fail(resource2); if(can1 != can2) { if(can1) { do_crm_log_unlikely(level, "%s > %s: failed", resource1->id, resource2->id); return 1; } do_crm_log_unlikely(level, "%s < %s: failed", resource1->id, resource2->id); return -1; } if(node1 && node2) { int max = 0; int lpc = 0; GListPtr list1 = g_list_append(NULL, node_copy(resource1->running_on->data)); GListPtr list2 = g_list_append(NULL, node_copy(resource2->running_on->data)); /* Possibly a replacement for the with_scores block above */ slist_iter( constraint, rsc_colocation_t, resource1->parent->rsc_cons_lhs, lpc, do_crm_log_unlikely(level+1, "Applying %s to %s", constraint->id, resource1->id); list1 = native_merge_weights( constraint->rsc_lh, resource1->id, list1, constraint->node_attribute, constraint->score/INFINITY, FALSE); ); slist_iter( constraint, rsc_colocation_t, resource2->parent->rsc_cons_lhs, lpc, do_crm_log_unlikely(level+1, "Applying %s to %s", constraint->id, resource2->id); list2 = native_merge_weights( constraint->rsc_lh, resource2->id, list2, constraint->node_attribute, constraint->score/INFINITY, FALSE); ); list1 = g_list_sort_with_data(list1, sort_node_weight, data_set); list2 = g_list_sort_with_data(list2, sort_node_weight, data_set); 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) { do_crm_log_unlikely(level, "%s < %s: colocated score NULL", resource1->id, resource2->id); pe_free_shallow(list1); pe_free_shallow(list2); return 1; } else if(node2 == NULL) { do_crm_log_unlikely(level, "%s > %s: colocated score NULL", resource1->id, resource2->id); pe_free_shallow(list1); pe_free_shallow(list2); return -1; } if(node1->weight < node2->weight) { do_crm_log_unlikely(level, "%s < %s: colocated score", resource1->id, resource2->id); pe_free_shallow(list1); pe_free_shallow(list2); return 1; } else if(node1->weight > node2->weight) { do_crm_log_unlikely(level, "%s > %s: colocated score", resource1->id, resource2->id); pe_free_shallow(list1); pe_free_shallow(list2); return -1; } } pe_free_shallow(list1); pe_free_shallow(list2); } do_crm_log_unlikely(level, "%s == %s: default %d", resource1->id, resource2->id, node2->weight); return 0; } static node_t * can_run_instance(resource_t *rsc, node_t *node) { node_t *local_node = NULL; clone_variant_data_t *clone_data = 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); get_clone_variant_data(clone_data, rsc->parent); 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->count < clone_data->clone_node_max) { return local_node; } else { crm_debug_2("%s cannot run on %s: node full", rsc->id, node->details->uname); } bail: if(node) { common_update_score(rsc, node->details->id, -INFINITY); } return NULL; } static node_t * color_instance(resource_t *rsc, pe_working_set_t *data_set) { node_t *chosen = NULL; node_t *local_node = NULL; crm_debug_2("Processing %s", rsc->id); 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)) { crm_debug("Dependency loop detected involving %s", rsc->id); return NULL; } if(rsc->allowed_nodes) { slist_iter(try_node, node_t, rsc->allowed_nodes, lpc, can_run_instance(rsc, try_node); ); } chosen = rsc->cmds->color(rsc, data_set); if(chosen) { local_node = pe_find_node_id( rsc->parent->allowed_nodes, chosen->details->id); 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_list_length(rsc->parent->allowed_nodes)); } } return chosen; } static void append_parent_colocation(resource_t *rsc, resource_t *child, gboolean all) { slist_iter(cons, rsc_colocation_t, rsc->rsc_cons, lpc, if(all || cons->score < 0 || cons->score == INFINITY) { child->rsc_cons = g_list_append(child->rsc_cons, cons); } ); slist_iter(cons, rsc_colocation_t, rsc->rsc_cons_lhs, lpc, if(all || cons->score < 0) { child->rsc_cons_lhs = g_list_append(child->rsc_cons_lhs, cons); } ); } node_t * clone_color(resource_t *rsc, pe_working_set_t *data_set) { int allocated = 0; int available_nodes = 0; 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)) { crm_debug("Dependency loop detected involving %s", rsc->id); return NULL; } set_bit(rsc->flags, pe_rsc_allocating); crm_debug_2("Processing %s", rsc->id); /* this information is used by sort_clone_instance() when deciding in which * order to allocate clone instances */ slist_iter( constraint, rsc_colocation_t, rsc->rsc_cons_lhs, lpc, rsc->allowed_nodes = constraint->rsc_lh->cmds->merge_weights( constraint->rsc_lh, rsc->id, rsc->allowed_nodes, constraint->node_attribute, constraint->score/INFINITY, TRUE); ); dump_node_scores(show_scores?0:scores_log_level, rsc, __FUNCTION__, rsc->allowed_nodes); /* count now tracks the number of clones currently allocated */ slist_iter(node, node_t, rsc->allowed_nodes, lpc, node->count = 0; ); slist_iter(child, resource_t, rsc->children, lpc, 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) { local_node->count++; } else { crm_err("%s is running on %s which isn't allowed", child->id, child_node->details->uname); } } ); rsc->children = g_list_sort_with_data(rsc->children, sort_clone_instance, data_set); /* count now tracks the number of clones we have allocated */ slist_iter(node, node_t, rsc->allowed_nodes, lpc, node->count = 0; ); rsc->allowed_nodes = g_list_sort_with_data( rsc->allowed_nodes, sort_node_weight, data_set); slist_iter(node, node_t, rsc->allowed_nodes, lpc, if(can_run_resources(node)) { available_nodes++; } ); slist_iter(child, resource_t, rsc->children, lpc, if(allocated >= clone_data->clone_max) { crm_debug("Child %s not allocated - limit reached", child->id); resource_location(child, NULL, -INFINITY, "clone_color:limit_reached", data_set); } else if (clone_data->clone_max < available_nodes) { /* Only include positive colocation preferences of dependant resources * if not every node will get a copy of the clone */ append_parent_colocation(rsc, child, TRUE); } else { append_parent_colocation(rsc, child, FALSE); } if(color_instance(child, data_set)) { allocated++; } ); crm_debug("Allocated %d %s instances of a possible %d", allocated, rsc->id, clone_data->clone_max); clear_bit(rsc->flags, pe_rsc_provisional); clear_bit(rsc->flags, pe_rsc_allocating); return NULL; } static void clone_update_pseudo_status( resource_t *rsc, gboolean *stopping, gboolean *starting, gboolean *active) { if(rsc->children) { slist_iter(child, resource_t, rsc->children, lpc, 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; } slist_iter( action, action_t, rsc->actions, lpc, if(*starting && *stopping) { return; } else if(action->optional) { crm_debug_3("Skipping optional: %s", action->uuid); continue; } else if(action->pseudo == FALSE && action->runnable == FALSE){ crm_debug_3("Skipping unrunnable: %s", action->uuid); continue; } else if(safe_str_eq(RSC_STOP, action->task)) { crm_debug_2("Stopping due to: %s", action->uuid); *stopping = TRUE; } else if(safe_str_eq(RSC_START, action->task)) { if(action->runnable == FALSE) { crm_debug_3("Skipping pseudo-op: %s run=%d, pseudo=%d", action->uuid, action->runnable, action->pseudo); } else { crm_debug_2("Starting due to: %s", action->uuid); crm_debug_3("%s run=%d, pseudo=%d", action->uuid, action->runnable, 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) { slist_iter(op, action_t, possible, lpc, if(op->optional == FALSE) { active = g_list_append(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; 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; } slist_iter( child, resource_t, rsc->children, lpc, key = stop_key(child); stop = find_rsc_action(child, key, active_only, NULL); crm_free(key); key = start_key(child); start = find_rsc_action(child, key, active_only, NULL); crm_free(key); if(stop) { if(last_stop) { /* child/child relative stop */ order_actions(stop, last_stop, pe_order_implies_left); } last_stop = stop; } if(start) { if(last_start) { /* child/child relative start */ order_actions(last_start, start, pe_order_implies_left); } last_start = start; } ); } void clone_create_actions(resource_t *rsc, pe_working_set_t *data_set) { gboolean child_active = FALSE; gboolean child_starting = FALSE; gboolean child_stopping = FALSE; action_t *stop = NULL; action_t *stopped = NULL; action_t *start = NULL; action_t *started = NULL; resource_t *last_start_rsc = NULL; resource_t *last_stop_rsc = NULL; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); crm_debug_2("Creating actions for %s", rsc->id); slist_iter( child_rsc, resource_t, rsc->children, lpc, child_rsc->cmds->create_actions(child_rsc, data_set); clone_update_pseudo_status( child_rsc, &child_stopping, &child_starting, &child_active); if(is_set(child_rsc->flags, pe_rsc_starting)) { last_start_rsc = child_rsc; } if(is_set(child_rsc->flags, pe_rsc_stopping)) { last_stop_rsc = child_rsc; } ); /* start */ start = start_action(rsc, NULL, !child_starting); started = custom_action(rsc, started_key(rsc), RSC_STARTED, NULL, !child_starting, TRUE, data_set); start->pseudo = TRUE; start->runnable = TRUE; started->pseudo = TRUE; started->priority = INFINITY; if(child_active || child_starting) { started->runnable = TRUE; } child_ordering_constraints(rsc, data_set); child_starting_constraints(clone_data, rsc, NULL, last_start_rsc, data_set); - clone_data->start_notify = create_notification_boundaries(rsc, RSC_START, start, started, data_set); - + if(clone_data->start_notify == NULL) { + clone_data->start_notify = create_notification_boundaries(rsc, RSC_START, start, started, data_set); + } + /* stop */ stop = stop_action(rsc, NULL, !child_stopping); stopped = custom_action(rsc, stopped_key(rsc), RSC_STOPPED, NULL, !child_stopping, TRUE, data_set); stop->pseudo = TRUE; stop->runnable = TRUE; stopped->pseudo = TRUE; stopped->runnable = TRUE; stopped->priority = INFINITY; child_stopping_constraints(clone_data, rsc, NULL, last_stop_rsc, data_set); - clone_data->stop_notify = create_notification_boundaries(rsc, RSC_STOP, stop, stopped, data_set); + if(clone_data->stop_notify == NULL) { + clone_data->stop_notify = create_notification_boundaries(rsc, RSC_STOP, stop, stopped, data_set); - if(clone_data->stop_notify && clone_data->start_notify) { - order_actions(clone_data->stop_notify->post_done, clone_data->start_notify->pre, pe_order_optional); + if(clone_data->stop_notify && clone_data->start_notify) { + order_actions(clone_data->stop_notify->post_done, clone_data->start_notify->pre, pe_order_optional); + } } } void child_starting_constraints( clone_variant_data_t *clone_data, resource_t *rsc, resource_t *child, resource_t *last, pe_working_set_t *data_set) { if(child == NULL && last == NULL) { crm_debug("%s has no active children", rsc->id); return; } if(child != NULL) { order_start_start( rsc, child, pe_order_runnable_left|pe_order_implies_left_printed); new_rsc_order(child, RSC_START, rsc, RSC_STARTED, pe_order_implies_right_printed, data_set); } if(FALSE && clone_data->ordered) { if(child == NULL) { /* last child start before global started */ new_rsc_order(last, RSC_START, rsc, RSC_STARTED, pe_order_runnable_left, data_set); } else if(last == NULL) { /* global start before first child start */ order_start_start( rsc, child, pe_order_implies_left); } else { /* child/child relative start */ order_start_start(last, child, pe_order_implies_left); } } } void child_stopping_constraints( clone_variant_data_t *clone_data, resource_t *rsc, resource_t *child, resource_t *last, pe_working_set_t *data_set) { if(child == NULL && last == NULL) { crm_debug("%s has no active children", rsc->id); return; } if(child != NULL) { order_stop_stop(rsc, child, pe_order_shutdown|pe_order_implies_left_printed); new_rsc_order(child, RSC_STOP, rsc, RSC_STOPPED, pe_order_implies_right_printed, data_set); } if(FALSE && clone_data->ordered) { if(last == NULL) { /* first child stop before global stopped */ new_rsc_order(child, RSC_STOP, rsc, RSC_STOPPED, pe_order_runnable_left, data_set); } else if(child == NULL) { /* global stop before last child stop */ order_stop_stop( rsc, last, pe_order_implies_left); } else { /* child/child relative stop */ order_stop_stop(child, last, pe_order_implies_left); } } } void clone_internal_constraints(resource_t *rsc, pe_working_set_t *data_set) { resource_t *last_rsc = NULL; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); native_internal_constraints(rsc, data_set); /* global stop before stopped */ new_rsc_order(rsc, RSC_STOP, rsc, RSC_STOPPED, pe_order_runnable_left, data_set); /* global start before started */ new_rsc_order(rsc, RSC_START, rsc, RSC_STARTED, pe_order_runnable_left, data_set); /* global stopped before start */ new_rsc_order(rsc, RSC_STOPPED, rsc, RSC_START, pe_order_optional, data_set); slist_iter( child_rsc, resource_t, rsc->children, lpc, child_rsc->cmds->internal_constraints(child_rsc, data_set); child_starting_constraints( clone_data, rsc, child_rsc, last_rsc, data_set); child_stopping_constraints( clone_data, rsc, child_rsc, last_rsc, data_set); last_rsc = child_rsc; ); } resource_t* find_compatible_child( resource_t *local_child, resource_t *rsc, enum rsc_role_e filter, gboolean current) { node_t *local_node = NULL; node_t *node = NULL; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); local_node = local_child->fns->location(local_child, NULL, current); if(local_node == NULL) { crm_debug("Can't colocate unrunnable child %s with %s", local_child->id, rsc->id); return NULL; } slist_iter( child_rsc, resource_t, rsc->children, lpc, enum rsc_role_e next_role = child_rsc->fns->state(child_rsc, current); node = child_rsc->fns->location(child_rsc, NULL, current); if(filter != RSC_ROLE_UNKNOWN && next_role != filter) { crm_debug_2("Filtered %s", child_rsc->id); continue; } if(node && local_node && node->details == local_node->details) { crm_info("Colocating %s with %s on %s", local_child->id, child_rsc->id, node->details->uname); return child_rsc; } ); crm_debug("Can't colocate child %s with %s", local_child->id, rsc->id); return NULL; } void clone_rsc_colocation_lh( resource_t *rsc_lh, resource_t *rsc_rh, rsc_colocation_t *constraint) { gboolean do_interleave = FALSE; resource_t *rsc = constraint->rsc_lh; clone_variant_data_t *clone_data = NULL; clone_variant_data_t *clone_data_rh = NULL; if(rsc == NULL) { pe_err("rsc_lh was NULL for %s", constraint->id); return; } else if(constraint->rsc_rh == NULL) { pe_err("rsc_rh was NULL for %s", constraint->id); return; } else { crm_debug_4("Processing constraints from %s", rsc->id); } get_clone_variant_data(clone_data, rsc); if(constraint->rsc_rh->variant == pe_clone || constraint->rsc_rh->variant == pe_master) { get_clone_variant_data( clone_data_rh, constraint->rsc_rh); if(clone_data->clone_node_max != clone_data_rh->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->interleave) { do_interleave = TRUE; } else if(constraint->score >= INFINITY) { GListPtr lhs = NULL, rhs = NULL; lhs = rsc_lh->allowed_nodes; slist_iter( child_rsc, resource_t, rsc_rh->children, lpc, node_t *chosen = child_rsc->fns->location(child_rsc, NULL, FALSE); if(chosen != NULL) { rhs = g_list_append(rhs, chosen); } ); rsc_lh->allowed_nodes = node_list_exclude(lhs, rhs, TRUE); pe_free_shallow_adv(rhs, FALSE); pe_free_shallow(lhs); return; } } else if(constraint->score >= INFINITY) { crm_config_err("Manditory co-location of clones (%s) with other" " non-clone (%s) resources is not supported", rsc_lh->id, rsc_rh->id); return; } if(do_interleave) { resource_t *rh_child = NULL; slist_iter(lh_child, resource_t, rsc->children, lpc, CRM_ASSERT(lh_child != NULL); rh_child = find_compatible_child( lh_child, rsc_rh, RSC_ROLE_UNKNOWN, FALSE); if(rh_child == NULL) { crm_debug_2("No match found for %s", lh_child->id); continue; } crm_debug("Interleaving %s with %s", lh_child->id, rh_child->id); lh_child->cmds->rsc_colocation_lh( lh_child, rh_child, constraint); ); return; } slist_iter( child_rsc, resource_t, rsc->children, lpc, child_rsc->cmds->rsc_colocation_lh(child_rsc, constraint->rsc_rh, constraint); ); } void clone_rsc_colocation_rh( resource_t *rsc_lh, resource_t *rsc_rh, rsc_colocation_t *constraint) { clone_variant_data_t *clone_data = NULL; CRM_CHECK(rsc_lh != NULL, return); CRM_CHECK(rsc_lh->variant == pe_native, return); get_clone_variant_data(clone_data, rsc_rh); crm_debug_3("Processing constraint %s: %s -> %s %d", constraint->id, rsc_lh->id, rsc_rh->id, constraint->score); if(rsc_rh == NULL) { pe_err("rsc_rh was NULL for %s", constraint->id); return; } else if(is_set(rsc_rh->flags, pe_rsc_provisional)) { crm_debug_3("%s is still provisional", rsc_rh->id); return; } else if(constraint->score >= INFINITY) { GListPtr lhs = NULL, rhs = NULL; lhs = rsc_lh->allowed_nodes; slist_iter( child_rsc, resource_t, rsc_rh->children, lpc, node_t *chosen = child_rsc->fns->location(child_rsc, NULL, FALSE); if(chosen != NULL) { rhs = g_list_append(rhs, chosen); } ); rsc_lh->allowed_nodes = node_list_exclude(lhs, rhs, FALSE); pe_free_shallow_adv(rhs, FALSE); pe_free_shallow(lhs); return; } slist_iter( child_rsc, resource_t, rsc_rh->children, lpc, child_rsc->cmds->rsc_colocation_rh(rsc_lh, child_rsc, constraint); ); } /* Clone <-> Clone ordering S : Start(ed) S' : Stop(ped) P : Promote(d) D : Demote(d) Started == Demoted First A then B A:0 B:0 Old New Old New S' S' S S' S' S' S' - S' S S S+ S' S S' S S S' S S' S S' S' - S S S - S S S' S S' S' P S' S' S' S' - S' P P P+ S' P S' P P S' P S' P S' S' - P P P - P P S' P D D P D D D D - D P P P+ D P D P P D P D P D D - P P P - P P D P Clone <-> Primitive ordering S : Start(ed) S' : Stop(ped) P : Promote(d) D : Demote(d) F : False T : True F' : A good idea? Started == Demoted First A then B A:0 B Old New Old Create Constraint S' S' S F S' S' S' F' S S' S T S S' S' F S' S S T S' S S' T S S S F' S S S' T S' S' S F S' S' S' F' P S' S T P S' S' F S' P S T S' P S' T P P S F' P P S' F S' S' S F S' S' S' F' D S' S T D S' S' F S' D S T S' D S' T D D S F' D D S' T */ static gboolean detect_restart(resource_t *rsc) { gboolean restart = FALSE; /* Look for restarts */ action_t *start = NULL; char *key = start_key(rsc); GListPtr possible_matches = find_actions(rsc->actions, key, NULL); crm_free(key); if(possible_matches) { start = possible_matches->data; g_list_free(possible_matches); } if(start != NULL && start->optional == FALSE) { restart = TRUE; crm_debug_2("Detected a restart for %s", rsc->id); } /* Otherwise, look for moves */ if(restart == FALSE) { GListPtr old_hosts = NULL; GListPtr new_hosts = NULL; GListPtr intersection = NULL; rsc->fns->location(rsc, &old_hosts, TRUE); rsc->fns->location(rsc, &new_hosts, FALSE); intersection = node_list_and(old_hosts, new_hosts, FALSE); if(intersection == NULL) { restart = TRUE; /* Actually a move but the result is the same */ crm_debug_2("Detected a move for %s", rsc->id); } - g_list_free(intersection); + slist_destroy(node_t, node, intersection, crm_free(node)); g_list_free(old_hosts); g_list_free(new_hosts); } return restart; } static void clone_rsc_order_lh_non_clone(resource_t *rsc, order_constraint_t *order, pe_working_set_t *data_set) { GListPtr hosts = NULL; GListPtr rh_hosts = NULL; GListPtr intersection = NULL; const char *reason = "unknown"; enum action_tasks task = start_rsc; enum rsc_role_e lh_role = RSC_ROLE_STARTED; int any_ordered = 0; gboolean down_stack = TRUE; crm_debug_2("Clone-to-* ordering: %s -> %s 0x%.6x", order->lh_action_task, order->rh_action_task, order->type); if(strstr(order->rh_action_task, "_"RSC_STOP"_0") || strstr(order->rh_action_task, "_"RSC_STOPPED"_0")) { task = stop_rsc; reason = "down activity"; lh_role = RSC_ROLE_STOPPED; order->rh_rsc->fns->location(order->rh_rsc, &rh_hosts, down_stack); } else if(strstr(order->rh_action_task, "_"RSC_DEMOTE"_0") || strstr(order->rh_action_task, "_"RSC_DEMOTED"_0")) { task = action_demote; reason = "demotion activity"; lh_role = RSC_ROLE_SLAVE; order->rh_rsc->fns->location(order->rh_rsc, &rh_hosts, down_stack); } else if(strstr(order->lh_action_task, "_"RSC_PROMOTE"_0") || strstr(order->lh_action_task, "_"RSC_PROMOTED"_0")) { task = action_promote; down_stack = FALSE; reason = "promote activity"; order->rh_rsc->fns->location(order->rh_rsc, &rh_hosts, down_stack); lh_role = RSC_ROLE_MASTER; } else if(strstr(order->rh_action_task, "_"RSC_START"_0") || strstr(order->rh_action_task, "_"RSC_STARTED"_0")) { task = start_rsc; down_stack = FALSE; reason = "up activity"; order->rh_rsc->fns->location(order->rh_rsc, &rh_hosts, down_stack); /* if(order->rh_rsc->variant > pe_clone) { */ /* lh_role = RSC_ROLE_SLAVE; */ /* } */ } else { crm_err("Unknown task: %s", order->rh_action_task); return; } /* slist_iter(h, node_t, rh_hosts, llpc, crm_info("RHH: %s", h->details->uname)); */ slist_iter( child_rsc, resource_t, rsc->children, lpc, gboolean create = FALSE; gboolean restart = FALSE; enum rsc_role_e lh_role_new = child_rsc->fns->state(child_rsc, FALSE); enum rsc_role_e lh_role_old = child_rsc->fns->state(child_rsc, TRUE); enum rsc_role_e child_role = child_rsc->fns->state(child_rsc, down_stack); crm_debug_4("Testing %s->%s for %s: %s vs. %s %s", order->lh_action_task, order->rh_action_task, child_rsc->id, role2text(lh_role), role2text(child_role), order->lh_action_task); if(rh_hosts == NULL) { crm_debug_3("Terminating search: %s.%d list is empty: no possible %s", order->rh_rsc->id, down_stack, reason); break; } if(lh_role_new == lh_role_old) { restart = detect_restart(child_rsc); if(restart == FALSE) { crm_debug_3("Ignoring %s->%s for %s: no relevant %s (no role change)", order->lh_action_task, order->rh_action_task, child_rsc->id, reason); continue; } } hosts = NULL; child_rsc->fns->location(child_rsc, &hosts, down_stack); intersection = node_list_and(hosts, rh_hosts, FALSE); /* slist_iter(h, node_t, hosts, llpc, crm_info("H: %s %s", child_rsc->id, h->details->uname)); */ if(intersection == NULL) { crm_debug_3("Ignoring %s->%s for %s: no relevant %s", order->lh_action_task, order->rh_action_task, child_rsc->id, reason); g_list_free(hosts); continue; } if(restart) { reason = "restart"; create = TRUE; } else if(down_stack) { if(lh_role_old > lh_role) { create = TRUE; } } else if(down_stack == FALSE) { if(lh_role_old < lh_role) { create = TRUE; } } else { any_ordered++; reason = "role"; crm_debug_4("Role: %s->%s for %s: %s vs. %s %s", order->lh_action_task, order->rh_action_task, child_rsc->id, role2text(lh_role_old), role2text(lh_role), order->lh_action_task); } if(create) { char *task = order->lh_action_task; any_ordered++; crm_debug("Enforcing %s->%s for %s on %s: found %s", order->lh_action_task, order->rh_action_task, child_rsc->id, ((node_t*)intersection->data)->details->uname, reason); order->lh_action_task = convert_non_atomic_task(task, child_rsc, TRUE, FALSE); child_rsc->cmds->rsc_order_lh(child_rsc, order, data_set); crm_free(order->lh_action_task); order->lh_action_task = task; } crm_debug_3("Processed %s->%s for %s on %s: %s", order->lh_action_task, order->rh_action_task, child_rsc->id, ((node_t*)intersection->data)->details->uname, reason); /* slist_iter(h, node_t, hosts, llpc, */ /* crm_info("H: %s %s", child_rsc->id, h->details->uname)); */ - g_list_free(intersection); + slist_destroy(node_t, node, intersection, crm_free(node)); g_list_free(hosts); ); g_list_free(rh_hosts); if(any_ordered == 0 && down_stack == FALSE) { GListPtr lh_hosts = NULL; if(order->type & pe_order_runnable_left) { rsc->fns->location(rsc, &lh_hosts, FALSE); } if(lh_hosts == NULL) { order->lh_action_task = convert_non_atomic_task(order->lh_action_task, rsc, TRUE, TRUE); native_rsc_order_lh(rsc, order, data_set); } g_list_free(lh_hosts); } order->type = pe_order_optional; } void clone_rsc_order_lh(resource_t *rsc, order_constraint_t *order, pe_working_set_t *data_set) { resource_t *r1 = NULL; resource_t *r2 = NULL; gboolean do_interleave = FALSE; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); crm_debug_4("%s->%s", order->lh_action_task, order->rh_action_task); if(order->rh_rsc == NULL) { order->lh_action_task = convert_non_atomic_task(order->lh_action_task, rsc, FALSE, TRUE); native_rsc_order_lh(rsc, order, data_set); return; } r1 = uber_parent(rsc); r2 = uber_parent(order->rh_rsc); if(r1 == r2) { native_rsc_order_lh(rsc, order, data_set); return; } - if(order->rh_rsc->variant > pe_group) { + if(order->rh_rsc->variant > pe_group && clone_data->interleave) { clone_variant_data_t *clone_data_rh = NULL; get_clone_variant_data(clone_data_rh, order->rh_rsc); - if(clone_data->clone_node_max != clone_data_rh->clone_node_max) { + + + if(clone_data->clone_node_max == clone_data_rh->clone_node_max) { + /* only the LHS side needs to be labeled as interleave */ + do_interleave = TRUE; + + } else { crm_config_err("Cannot interleave "XML_CIB_TAG_INCARNATION " %s and %s because they do not support the same" " number of resources per node", rsc->id, order->rh_rsc->id); - - /* only the LHS side needs to be labeled as interleave */ - } else if(clone_data->interleave) { - do_interleave = TRUE; } } if(order->rh_rsc == NULL) { do_interleave = FALSE; } if(do_interleave) { resource_t *lh_child = NULL; resource_t *rh_saved = order->rh_rsc; gboolean current = FALSE; if(strstr(order->lh_action_task, "_stop_0") || strstr(order->lh_action_task, "_demote_0")) { current = TRUE; } slist_iter( rh_child, resource_t, rh_saved->children, lpc, CRM_ASSERT(rh_child != NULL); lh_child = find_compatible_child(rh_child, rsc, RSC_ROLE_UNKNOWN, current); if(lh_child == NULL) { crm_debug_2("No match found for %s", rh_child->id); continue; } crm_notice("Interleaving %s with %s", lh_child->id, rh_child->id); order->rh_rsc = rh_child; lh_child->cmds->rsc_order_lh(lh_child, order, data_set); order->rh_rsc = rh_saved; ); } else { #if 0 if(order->type != pe_order_optional) { crm_debug("Upgraded ordering constraint %d - 0x%.6x", order->id, order->type); native_rsc_order_lh(rsc, order, data_set); } #endif if(order->rh_rsc->variant < pe_clone) { clone_rsc_order_lh_non_clone(rsc, order, data_set); } else if(order->type & pe_order_implies_left) { if(rsc->variant == order->rh_rsc->variant) { crm_debug_2("Clone-to-clone ordering: %s -> %s 0x%.6x", order->lh_action_task, order->rh_action_task, order->type); /* stop instances on the same nodes as stopping RHS instances */ slist_iter( child_rsc, resource_t, rsc->children, lpc, native_rsc_order_lh(child_rsc, order, data_set); ); } else { /* stop everything */ slist_iter( child_rsc, resource_t, rsc->children, lpc, native_rsc_order_lh(child_rsc, order, data_set); ); } } } if(do_interleave == FALSE || clone_data->ordered) { order->lh_action_task = convert_non_atomic_task(order->lh_action_task, rsc, FALSE, TRUE); native_rsc_order_lh(rsc, order, data_set); } if(is_set(rsc->flags, pe_rsc_notify)) { order->type = pe_order_optional; order->lh_action_task = convert_non_atomic_task(order->lh_action_task, rsc, TRUE, TRUE); native_rsc_order_lh(rsc, order, data_set); } } static void clone_rsc_order_rh_non_clone( resource_t *lh_p, action_t *lh_action, resource_t *rsc, order_constraint_t *order) { GListPtr hosts = NULL; GListPtr lh_hosts = NULL; GListPtr intersection = NULL; const char *reason = "unknown"; gboolean restart = FALSE; gboolean down_stack = TRUE; enum rsc_role_e rh_role = RSC_ROLE_STARTED; enum action_tasks task = start_rsc; enum rsc_role_e lh_role_new = lh_p->fns->state(lh_p, FALSE); enum rsc_role_e lh_role_old = lh_p->fns->state(lh_p, TRUE); /* Make sure the pre-req will be active */ if(order->type & pe_order_runnable_left) { lh_p->fns->location(lh_p, &lh_hosts, FALSE); if(lh_hosts == NULL) { crm_debug("Terminating search: Pre-requisite %s of %s is unrunnable", lh_p->id, rsc->id); native_rsc_order_rh(lh_action, rsc, order); return; } g_list_free(lh_hosts); lh_hosts = NULL; } if(strstr(order->lh_action_task, "_"RSC_STOP"_0") || strstr(order->lh_action_task, "_"RSC_STOPPED"_0")) { task = stop_rsc; reason = "down activity"; rh_role = RSC_ROLE_STOPPED; lh_p->fns->location(lh_p, &lh_hosts, down_stack); /* These actions are not possible for non-clones } else if(strstr(order->lh_action_task, "_"RSC_DEMOTE"_0") || strstr(order->lh_action_task, "_"RSC_DEMOTED"_0")) { task = action_demote; rh_role = RSC_ROLE_SLAVE; reason = "demotion activity"; lh_p->fns->location(lh_p, &lh_hosts, down_stack); } else if(strstr(order->lh_action_task, "_"RSC_PROMOTE"_0") || strstr(order->lh_action_task, "_"RSC_PROMOTED"_0")) { task = action_promote; down_stack = FALSE; reason = "promote activity"; lh_p->fns->location(lh_p, &lh_hosts, down_stack); rh_role = RSC_ROLE_MASTER; */ } else if(strstr(order->lh_action_task, "_"RSC_START"_0") || strstr(order->lh_action_task, "_"RSC_STARTED"_0")) { task = start_rsc; down_stack = FALSE; reason = "up activity"; lh_p->fns->location(lh_p, &lh_hosts, down_stack); } else { crm_err("Unknown action: %s", order->lh_action_task); return; } if(lh_role_new == lh_role_old) { restart = detect_restart(lh_action->rsc); if(FALSE && restart == FALSE) { crm_debug_3("Ignoring %s->%s for %s: no relevant %s (no role change)", lh_action->task, order->lh_action_task, lh_p->id, reason); goto cleanup; } } /* slist_iter(h, node_t, lh_hosts, llpc, crm_info("LHH: %s", h->details->uname)); */ slist_iter( child_rsc, resource_t, rsc->children, lpc, gboolean create = FALSE; enum rsc_role_e child_role = child_rsc->fns->state(child_rsc, down_stack); crm_debug_4("Testing %s->%s for %s: %s vs. %s %s", lh_action->task, order->lh_action_task, child_rsc->id, role2text(rh_role), role2text(child_role), order->lh_action_task); if(lh_hosts == NULL) { crm_debug_3("Terminating search: %s.%d list is empty: no possible %s", order->rh_rsc->id, down_stack, reason); break; } hosts = NULL; child_rsc->fns->location(child_rsc, &hosts, down_stack); intersection = node_list_and(hosts, lh_hosts, FALSE); if(intersection == NULL) { crm_debug_3("Ignoring %s->%s for %s: no relevant %s", lh_action->task, order->lh_action_task, child_rsc->id, reason); g_list_free(hosts); continue; } /* slist_iter(h, node_t, hosts, llpc, crm_info("H: %s %s", child_rsc->id, h->details->uname)); */ if(restart) { reason = "restart"; create = TRUE; } else if(down_stack && lh_role_old >= rh_role) { create = TRUE; } else if(down_stack == FALSE && lh_role_old <= rh_role) { create = TRUE; } else { reason = "role"; } if(create) { enum pe_ordering type = order->type; child_rsc->cmds->rsc_order_rh(lh_action, child_rsc, order); order->type = pe_order_optional; native_rsc_order_rh(lh_action, rsc, order); order->type = type; } crm_debug_3("Processed %s->%s for %s on %s: found %s%s", lh_action->task, order->lh_action_task, child_rsc->id, ((node_t*)intersection->data)->details->uname, reason, create?" - enforced":""); /* slist_iter(h, node_t, hosts, llpc, */ /* crm_info("H: %s %s", child_rsc->id, h->details->uname)); */ - g_list_free(intersection); + slist_destroy(node_t, node, intersection, crm_free(node)); g_list_free(hosts); ); cleanup: g_list_free(lh_hosts); } void clone_rsc_order_rh( action_t *lh_action, resource_t *rsc, order_constraint_t *order) { enum pe_ordering type = order->type; clone_variant_data_t *clone_data = NULL; resource_t *lh_p = uber_parent(lh_action->rsc); get_clone_variant_data(clone_data, rsc); crm_debug_2("%s->%s", order->lh_action_task, order->rh_action_task); if(safe_str_eq(CRM_OP_PROBED, lh_action->uuid)) { slist_iter( child_rsc, resource_t, rsc->children, lpc, child_rsc->cmds->rsc_order_rh(lh_action, child_rsc, order); ); if(rsc->fns->state(rsc, TRUE) < RSC_ROLE_STARTED && rsc->fns->state(rsc, FALSE) > RSC_ROLE_STOPPED) { order->type |= pe_order_implies_right; } } else if(lh_p && lh_p != rsc && lh_p->variant < pe_clone) { clone_rsc_order_rh_non_clone(lh_p, lh_action, rsc, order); return; } native_rsc_order_rh(lh_action, rsc, order); order->type = type; } void clone_rsc_location(resource_t *rsc, rsc_to_node_t *constraint) { clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); crm_debug_3("Processing location constraint %s for %s", constraint->id, rsc->id); native_rsc_location(rsc, constraint); slist_iter( child_rsc, resource_t, rsc->children, lpc, child_rsc->cmds->rsc_location(child_rsc, constraint); ); } void clone_expand(resource_t *rsc, pe_working_set_t *data_set) { clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); - crm_debug_2("Processing actions from %s", rsc->id); + crm_err("Processing actions from %s", rsc->id); if(clone_data->start_notify) { collect_notification_data(rsc, TRUE, TRUE, clone_data->start_notify); expand_notification_data(clone_data->start_notify); 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); 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); 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); create_notifications(rsc, clone_data->demote_notify, data_set); } /* Now that the notifcations have been created we can expand the children */ slist_iter( child_rsc, resource_t, rsc->children, lpc, 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); - free_notification_data(clone_data->stop_notify); - free_notification_data(clone_data->start_notify); - free_notification_data(clone_data->promote_notify); + 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; } 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 resource_t *find_instance_on(resource_t *rsc, node_t *node) { slist_iter(child, resource_t, rsc->children, lpc, GListPtr known_list = NULL; rsc_known_on(child, &known_list); slist_iter(known, node_t, known_list, lpc2, 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) { gboolean any_created = FALSE; clone_variant_data_t *clone_data = NULL; 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(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 */ slist_iter( child_rsc, resource_t, rsc->children, lpc, node_t *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 */ child = rsc->children->data; return child->cmds->create_probe(child, node, complete, force, data_set); } slist_iter( child_rsc, resource_t, rsc->children, lpc, 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"); crm_free(name); name = crm_meta_name(XML_RSC_ATTR_NOTIFY); crm_xml_add(xml, name, is_set(rsc->flags, pe_rsc_notify)?"true":"false"); crm_free(name); name = crm_meta_name(XML_RSC_ATTR_INCARNATION_MAX); crm_xml_add_int(xml, name, clone_data->clone_max); crm_free(name); name = crm_meta_name(XML_RSC_ATTR_INCARNATION_NODEMAX); crm_xml_add_int(xml, name, clone_data->clone_node_max); crm_free(name); } diff --git a/pengine/constraints.c b/pengine/constraints.c index 5c45897bba..42b5729777 100644 --- a/pengine/constraints.c +++ b/pengine/constraints.c @@ -1,1278 +1,1286 @@ /* * 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.1 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include enum pe_order_kind { pe_order_kind_optional, pe_order_kind_mandatory, pe_order_kind_serialize, }; enum pe_ordering get_flags( const char *id, enum pe_order_kind kind, const char *action_first, const char *action_then, gboolean invert); gboolean unpack_constraints(xmlNode * xml_constraints, pe_working_set_t *data_set) { xmlNode *lifetime = NULL; xml_child_iter( xml_constraints, xml_obj, const char *id = crm_element_value(xml_obj, XML_ATTR_ID); if(id == NULL) { crm_config_err("Constraint <%s...> must have an id", crm_element_name(xml_obj)); continue; } crm_debug_3("Processing constraint %s %s", crm_element_name(xml_obj),id); lifetime = first_named_child(xml_obj, "lifetime"); if(lifetime) { crm_config_warn("Support for the lifetime tag, used by %s, is deprecated." " The rules it contains should instead be direct decendants of the constraint object", id); } if(test_ruleset(lifetime, NULL, data_set->now) == FALSE) { crm_info("Constraint %s %s is not active", crm_element_name(xml_obj), id); } else if(safe_str_eq(XML_CONS_TAG_RSC_ORDER, crm_element_name(xml_obj))) { unpack_rsc_order(xml_obj, data_set); } else if(safe_str_eq(XML_CONS_TAG_RSC_DEPEND, crm_element_name(xml_obj))) { unpack_rsc_colocation(xml_obj, data_set); } else if(safe_str_eq(XML_CONS_TAG_RSC_LOCATION, crm_element_name(xml_obj))) { unpack_rsc_location(xml_obj, data_set); } else { pe_err("Unsupported constraint type: %s", crm_element_name(xml_obj)); } ); return TRUE; } static const char * invert_action(const char *action) { if(safe_str_eq(action, RSC_START)) { return RSC_STOP; } else if(safe_str_eq(action, RSC_STOP)) { return RSC_START; } else if(safe_str_eq(action, RSC_PROMOTE)) { return RSC_DEMOTE; } else if(safe_str_eq(action, RSC_DEMOTE)) { return RSC_PROMOTE; } else if(safe_str_eq(action, RSC_PROMOTED)) { return RSC_DEMOTED; } else if(safe_str_eq(action, RSC_DEMOTED)) { return RSC_PROMOTED; } else if(safe_str_eq(action, RSC_STARTED)) { return RSC_STOPPED; } else if(safe_str_eq(action, RSC_STOPPED)) { return RSC_STARTED; } crm_config_warn("Unknown action: %s", action); return NULL; } static enum pe_order_kind get_ordering_type(xmlNode *xml_obj) { enum pe_order_kind kind_e = pe_order_kind_mandatory; const char *kind = crm_element_value(xml_obj, XML_ORDER_ATTR_KIND); if(kind == NULL) { const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE); kind_e = pe_order_kind_mandatory; if(score) { int score_i = char2score(score); if(score_i == 0) { kind_e = pe_order_kind_optional; } /* } else if(rsc_then->variant == pe_native && rsc_first->variant > pe_group) { */ /* kind_e = pe_order_kind_optional; */ } } else if(safe_str_eq(kind, "Mandatory")) { kind_e = pe_order_kind_mandatory; } else if(safe_str_eq(kind, "Optional")) { kind_e = pe_order_kind_optional; } else if(safe_str_eq(kind, "Serialize")) { kind_e = pe_order_kind_serialize; } else { const char *id = crm_element_value(xml_obj, XML_ATTR_ID); crm_config_err("Constraint %s: Unknown type '%s'", id, kind); } return kind_e; } static gboolean unpack_simple_rsc_order(xmlNode * xml_obj, pe_working_set_t *data_set) { int order_id = 0; resource_t *rsc_then = NULL; resource_t *rsc_first = NULL; gboolean invert_bool = TRUE; enum pe_order_kind kind = pe_order_kind_mandatory; enum pe_ordering cons_weight = pe_order_optional; const char *id_first = NULL; const char *id_then = NULL; const char *action_then = NULL; const char *action_first = NULL; const char *instance_then = NULL; const char *instance_first = NULL; const char *id = crm_element_value(xml_obj, XML_ATTR_ID); const char *invert = crm_element_value(xml_obj, XML_CONS_ATTR_SYMMETRICAL); crm_str_to_boolean(invert, &invert_bool); if(xml_obj == NULL) { crm_config_err("No constraint object to process."); return FALSE; } else if(id == NULL) { crm_config_err("%s constraint must have an id", crm_element_name(xml_obj)); return FALSE; } id_then = crm_element_value(xml_obj, XML_ORDER_ATTR_THEN); id_first = crm_element_value(xml_obj, XML_ORDER_ATTR_FIRST); action_then = crm_element_value(xml_obj, XML_ORDER_ATTR_THEN_ACTION); action_first = crm_element_value(xml_obj, XML_ORDER_ATTR_FIRST_ACTION); instance_then = crm_element_value(xml_obj, XML_ORDER_ATTR_THEN_INSTANCE); instance_first = crm_element_value(xml_obj, XML_ORDER_ATTR_FIRST_INSTANCE); if(action_first == NULL) { action_first = RSC_START; } if(action_then == NULL) { action_then = action_first; } if(id_then == NULL || id_first == NULL) { crm_config_err("Constraint %s needs two sides lh: %s rh: %s", id, crm_str(id_then), crm_str(id_first)); return FALSE; } rsc_then = pe_find_resource(data_set->resources, id_then); rsc_first = pe_find_resource(data_set->resources, id_first); if(rsc_then == NULL) { crm_config_err("Constraint %s: no resource found for name '%s'", id, id_then); return FALSE; } else if(rsc_first == NULL) { crm_config_err("Constraint %s: no resource found for name '%s'", id, id_first); return FALSE; } else if(instance_then && rsc_then->variant < pe_clone) { crm_config_err("Invalid constraint '%s':" " Resource '%s' is not a clone but instance %s was requested", id, id_then, instance_then); return FALSE; } else if(instance_first && rsc_first->variant < pe_clone) { crm_config_err("Invalid constraint '%s':" " Resource '%s' is not a clone but instance %s was requested", id, id_first, instance_first); return FALSE; } if(instance_then) { rsc_then = find_clone_instance(rsc_then, instance_then, data_set); if(rsc_then == NULL) { crm_config_warn("Invalid constraint '%s': No instance '%s' of '%s'", id, instance_then, id_then); return FALSE; } } if(instance_first) { rsc_first = find_clone_instance(rsc_first, instance_first, data_set); if(rsc_first == NULL) { crm_config_warn("Invalid constraint '%s': No instance '%s' of '%s'", id, instance_first, id_first); return FALSE; } } cons_weight = pe_order_optional; kind = get_ordering_type(xml_obj); if(kind == pe_order_kind_optional && rsc_then->restart_type == pe_restart_restart) { crm_debug_2("Upgrade : recovery - implies right"); cons_weight |= pe_order_implies_right; } cons_weight |= get_flags(id, kind, action_first, action_then, FALSE); order_id = new_rsc_order( rsc_first, action_first, rsc_then, action_then, cons_weight, data_set); crm_debug_2("order-%d (%s): %s_%s before %s_%s flags=0x%.6x", order_id, id, rsc_first->id, action_first, rsc_then->id, action_then, cons_weight); if(invert_bool == FALSE) { return TRUE; } else if(invert && kind == pe_order_kind_serialize) { crm_config_warn("Cannot invert serialized constraint set %s", id); return TRUE; } else if(kind == pe_order_kind_serialize) { return TRUE; } action_then = invert_action(action_then); action_first = invert_action(action_first); if(action_then == NULL || action_first == NULL) { crm_config_err("Cannot invert rsc_order constraint %s." " Please specify the inverse manually.", id); return TRUE; } cons_weight = pe_order_optional; if(kind == pe_order_kind_optional && rsc_then->restart_type == pe_restart_restart) { crm_debug_2("Upgrade : recovery - implies left"); cons_weight |= pe_order_implies_left; } cons_weight |= get_flags(id, kind, action_first, action_then, TRUE); order_id = new_rsc_order( rsc_then, action_then, rsc_first, action_first, cons_weight, data_set); crm_debug_2("order-%d (%s): %s_%s before %s_%s flags=0x%.6x", order_id, id, rsc_then->id, action_then, rsc_first->id, action_first, cons_weight); return TRUE; } gboolean unpack_rsc_location(xmlNode * xml_obj, pe_working_set_t *data_set) { gboolean empty = TRUE; rsc_to_node_t *location = NULL; const char *id_lh = crm_element_value(xml_obj, "rsc"); const char *id = crm_element_value(xml_obj, XML_ATTR_ID); resource_t *rsc_lh = pe_find_resource(data_set->resources, id_lh); const char *node = crm_element_value(xml_obj, "node"); const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE); const char *domain = crm_element_value(xml_obj, XML_CIB_TAG_DOMAIN); const char *role = crm_element_value(xml_obj, XML_RULE_ATTR_ROLE); if(rsc_lh == NULL) { /* only a warn as BSC adds the constraint then the resource */ crm_config_warn("No resource (con=%s, rsc=%s)", id, id_lh); return FALSE; } if(domain) { GListPtr nodes = g_hash_table_lookup(data_set->domains, domain); if(domain == NULL) { crm_config_err("Invalid constraint %s: Domain %s does not exist", id, domain); return FALSE; } location = rsc2node_new(id, rsc_lh, 0, NULL, data_set); location->node_list_rh = node_list_dup(nodes, FALSE, FALSE); } else if(node != NULL && score != NULL) { int score_i = char2score(score); node_t *match = pe_find_node(data_set->nodes, node); if(!match) { return FALSE; } location = rsc2node_new(id, rsc_lh, score_i, match, data_set); } else { xml_child_iter_filter( xml_obj, rule_xml, XML_TAG_RULE, empty = FALSE; crm_debug_2("Unpacking %s/%s", id, ID(rule_xml)); generate_location_rule(rsc_lh, rule_xml, data_set); ); if(empty) { crm_config_err("Invalid location constraint %s:" " rsc_location must contain at least one rule", ID(xml_obj)); } } if(location && role) { if(text2role(role) == RSC_ROLE_UNKNOWN) { pe_err("Invalid constraint %s: Bad role %s", id, role); return FALSE; } else { location->role_filter = text2role(role); if(location->role_filter == RSC_ROLE_SLAVE) { /* Fold slave back into Started for simplicity * At the point Slave location constraints are evaluated, * all resources are still either stopped or started */ location->role_filter = RSC_ROLE_STARTED; } } } return TRUE; } static int get_node_score(const char *rule, const char *score, gboolean raw, node_t *node) { int score_f = 0; if(score == NULL) { pe_err("Rule %s: no score specified. Assuming 0.", rule); } else if(raw) { score_f = char2score(score); } else { const char *attr_score = g_hash_table_lookup( node->details->attrs, score); if(attr_score == NULL) { crm_debug("Rule %s: node %s did not have a value for %s", rule, node->details->uname, score); score_f = -INFINITY; } else { crm_debug("Rule %s: node %s had value %s for %s", rule, node->details->uname, attr_score, score); score_f = char2score(attr_score); } } return score_f; } rsc_to_node_t * generate_location_rule( resource_t *rsc, xmlNode *rule_xml, pe_working_set_t *data_set) { const char *rule_id = NULL; const char *score = NULL; const char *boolean = NULL; const char *role = NULL; GListPtr match_L = NULL; int score_f = 0; gboolean do_and = TRUE; gboolean accept = TRUE; gboolean raw_score = TRUE; rsc_to_node_t *location_rule = NULL; rule_xml = expand_idref(rule_xml, data_set->input); rule_id = crm_element_value(rule_xml, XML_ATTR_ID); boolean = crm_element_value(rule_xml, XML_RULE_ATTR_BOOLEAN_OP); role = crm_element_value(rule_xml, XML_RULE_ATTR_ROLE); crm_debug_2("Processing rule: %s", rule_id); if(role != NULL && text2role(role) == RSC_ROLE_UNKNOWN) { pe_err("Bad role specified for %s: %s", rule_id, role); return NULL; } score = crm_element_value(rule_xml, XML_RULE_ATTR_SCORE); if(score != NULL) { score_f = char2score(score); } else { score = crm_element_value( rule_xml, XML_RULE_ATTR_SCORE_ATTRIBUTE); if(score == NULL) { score = crm_element_value( rule_xml, XML_RULE_ATTR_SCORE_MANGLED); } if(score != NULL) { raw_score = FALSE; } } if(safe_str_eq(boolean, "or")) { do_and = FALSE; } location_rule = rsc2node_new(rule_id, rsc, 0, NULL, data_set); if(location_rule == NULL) { return NULL; } if(role != NULL) { crm_debug_2("Setting role filter: %s", role); location_rule->role_filter = text2role(role); if(location_rule->role_filter == RSC_ROLE_SLAVE) { /* Fold slave back into Started for simplicity * At the point Slave location constraints are evaluated, * all resources are still either stopped or started */ location_rule->role_filter = RSC_ROLE_STARTED; } } if(do_and) { match_L = node_list_dup(data_set->nodes, TRUE, FALSE); slist_iter( node, node_t, match_L, lpc, node->weight = get_node_score(rule_id, score, raw_score, node); ); } slist_iter( node, node_t, data_set->nodes, lpc, accept = test_rule( rule_xml, node->details->attrs, RSC_ROLE_UNKNOWN, data_set->now); crm_debug_2("Rule %s %s on %s", ID(rule_xml), accept?"passed":"failed", node->details->uname); score_f = get_node_score(rule_id, score, raw_score, node); /* if(accept && score_f == -INFINITY) { */ /* accept = FALSE; */ /* } */ if(accept) { node_t *local = pe_find_node_id( match_L, node->details->id); if(local == NULL && do_and) { continue; } else if(local == NULL) { local = node_copy(node); match_L = g_list_append(match_L, local); } if(do_and == FALSE) { local->weight = merge_weights( local->weight, score_f); } crm_debug_2("node %s now has weight %d", node->details->uname, local->weight); } else if(do_and && !accept) { /* remove it */ node_t *delete = pe_find_node_id( match_L, node->details->id); if(delete != NULL) { match_L = g_list_remove(match_L,delete); crm_debug_5("node %s did not match", node->details->uname); } crm_free(delete); } ); location_rule->node_list_rh = match_L; if(location_rule->node_list_rh == NULL) { crm_debug_2("No matching nodes for rule %s", rule_id); return NULL; } crm_debug_3("%s: %d nodes matched", rule_id, g_list_length(location_rule->node_list_rh)); return location_rule; } static gint sort_cons_priority_lh(gconstpointer a, gconstpointer b) { const rsc_colocation_t *rsc_constraint1 = (const rsc_colocation_t*)a; const rsc_colocation_t *rsc_constraint2 = (const rsc_colocation_t*)b; if(a == NULL) { return 1; } if(b == NULL) { return -1; } CRM_ASSERT(rsc_constraint1->rsc_lh != NULL); CRM_ASSERT(rsc_constraint1->rsc_rh != NULL); if(rsc_constraint1->rsc_lh->priority > rsc_constraint2->rsc_lh->priority) { return -1; } if(rsc_constraint1->rsc_lh->priority < rsc_constraint2->rsc_lh->priority) { return 1; } return strcmp(rsc_constraint1->rsc_lh->id, rsc_constraint2->rsc_lh->id); } static gint sort_cons_priority_rh(gconstpointer a, gconstpointer b) { const rsc_colocation_t *rsc_constraint1 = (const rsc_colocation_t*)a; const rsc_colocation_t *rsc_constraint2 = (const rsc_colocation_t*)b; if(a == NULL) { return 1; } if(b == NULL) { return -1; } CRM_ASSERT(rsc_constraint1->rsc_lh != NULL); CRM_ASSERT(rsc_constraint1->rsc_rh != NULL); if(rsc_constraint1->rsc_rh->priority > rsc_constraint2->rsc_rh->priority) { return -1; } if(rsc_constraint1->rsc_rh->priority < rsc_constraint2->rsc_rh->priority) { return 1; } return strcmp(rsc_constraint1->rsc_rh->id, rsc_constraint2->rsc_rh->id); } 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) { rsc_colocation_t *new_con = NULL; if(rsc_lh == NULL){ crm_config_err("No resource found for LHS %s", id); return FALSE; } else if(rsc_rh == NULL){ crm_config_err("No resource found for RHS of %s", id); return FALSE; } crm_malloc0(new_con, sizeof(rsc_colocation_t)); if(new_con == NULL) { return FALSE; } if(state_lh == NULL || safe_str_eq(state_lh, RSC_ROLE_STARTED_S)) { state_lh = RSC_ROLE_UNKNOWN_S; } if(state_rh == NULL || safe_str_eq(state_rh, RSC_ROLE_STARTED_S)) { state_rh = RSC_ROLE_UNKNOWN_S; } new_con->id = id; new_con->rsc_lh = rsc_lh; new_con->rsc_rh = rsc_rh; new_con->score = score; new_con->role_lh = text2role(state_lh); new_con->role_rh = text2role(state_rh); new_con->node_attribute = node_attr; if(node_attr == NULL) { node_attr = "#"XML_ATTR_UNAME; } crm_debug_3("%s ==> %s (%s %d)", rsc_lh->id, rsc_rh->id, node_attr, score); rsc_lh->rsc_cons = g_list_insert_sorted( rsc_lh->rsc_cons, new_con, sort_cons_priority_rh); rsc_rh->rsc_cons_lhs = g_list_insert_sorted( rsc_rh->rsc_cons_lhs, new_con, sort_cons_priority_lh); data_set->colocation_constraints = g_list_append( data_set->colocation_constraints, new_con); return TRUE; } /* LHS before RHS */ int new_rsc_order(resource_t *lh_rsc, const char *lh_task, resource_t *rh_rsc, const char *rh_task, enum pe_ordering type, pe_working_set_t *data_set) { char *lh_key = NULL; char *rh_key = NULL; CRM_CHECK(lh_rsc != NULL, return -1); CRM_CHECK(lh_task != NULL, return -1); CRM_CHECK(rh_rsc != NULL, return -1); CRM_CHECK(rh_task != NULL, return -1); lh_key = generate_op_key(lh_rsc->id, lh_task, 0); rh_key = generate_op_key(rh_rsc->id, rh_task, 0); return custom_action_order(lh_rsc, lh_key, NULL, rh_rsc, rh_key, NULL, type, data_set); } /* LHS before RHS */ int custom_action_order( resource_t *lh_rsc, char *lh_action_task, action_t *lh_action, resource_t *rh_rsc, char *rh_action_task, action_t *rh_action, enum pe_ordering type, pe_working_set_t *data_set) { order_constraint_t *order = NULL; if(lh_rsc == NULL && lh_action) { lh_rsc = lh_action->rsc; } if(rh_rsc == NULL && rh_action) { rh_rsc = rh_action->rsc; } if((lh_action == NULL && lh_rsc == NULL) || (rh_action == NULL && rh_rsc == NULL)){ crm_config_err("Invalid inputs %p.%p %p.%p", lh_rsc, lh_action, rh_rsc, rh_action); crm_free(lh_action_task); crm_free(rh_action_task); return -1; } crm_malloc0(order, sizeof(order_constraint_t)); crm_debug_3("Creating ordering constraint %d", data_set->order_id); order->id = data_set->order_id++; order->type = type; order->lh_rsc = lh_rsc; order->rh_rsc = rh_rsc; order->lh_action = lh_action; order->rh_action = rh_action; order->lh_action_task = lh_action_task; order->rh_action_task = rh_action_task; data_set->ordering_constraints = g_list_append( data_set->ordering_constraints, order); if(lh_rsc != NULL && rh_rsc != NULL) { crm_debug_4("Created ordering constraint %d (%s):" " %s/%s before %s/%s", order->id, ordering_type2text(order->type), lh_rsc->id, lh_action_task, rh_rsc->id, rh_action_task); } else if(lh_rsc != NULL) { crm_debug_4("Created ordering constraint %d (%s):" " %s/%s before action %d (%s)", order->id, ordering_type2text(order->type), lh_rsc->id, lh_action_task, rh_action->id, rh_action_task); } else if(rh_rsc != NULL) { crm_debug_4("Created ordering constraint %d (%s):" " action %d (%s) before %s/%s", order->id, ordering_type2text(order->type), lh_action->id, lh_action_task, rh_rsc->id, rh_action_task); } else { crm_debug_4("Created ordering constraint %d (%s):" " action %d (%s) before action %d (%s)", order->id, ordering_type2text(order->type), lh_action->id, lh_action_task, rh_action->id, rh_action_task); } return order->id; } enum pe_ordering get_flags( const char *id, enum pe_order_kind kind, const char *action_first, const char *action_then, gboolean invert) { enum pe_ordering flags = pe_order_optional; if(invert && kind == pe_order_kind_mandatory) { crm_debug_2("Upgrade %s: implies left", id); flags |= pe_order_implies_left; if(safe_str_eq(action_then, RSC_DEMOTE)) { crm_debug_2("Upgrade %s: demote", id); flags |= pe_order_demote; } } else if(kind == pe_order_kind_mandatory) { crm_debug_2("Upgrade %s: implies right", id); flags |= pe_order_implies_right; if(safe_str_eq(action_first, RSC_START) || safe_str_eq(action_first, RSC_PROMOTE)) { crm_debug_2("Upgrade %s: runnable", id); flags |= pe_order_runnable_left; } } else if(kind == pe_order_kind_serialize) { flags |= pe_order_serialize_only; } return flags; } static gboolean unpack_order_set(xmlNode *set, enum pe_order_kind kind, resource_t **rsc, action_t **begin, action_t **end, action_t **inv_begin, action_t **inv_end, const char *symmetrical, pe_working_set_t *data_set) { GListPtr set_iter = NULL; GListPtr resources = NULL; resource_t *last = NULL; resource_t *resource = NULL; int local_kind = kind; gboolean sequential = FALSE; enum pe_ordering flags = pe_order_optional; char *key = NULL; const char *id = ID(set); const char *action = crm_element_value(set, "action"); const char *sequential_s = crm_element_value(set, "sequential"); const char *kind_s = crm_element_value(set, XML_ORDER_ATTR_KIND); char *pseudo_id = NULL; char *end_id = NULL; char *begin_id = NULL; if(action == NULL) { action = RSC_START; } if(kind_s) { local_kind = get_ordering_type(set); } if(sequential_s == NULL) { sequential_s = "1"; } sequential = crm_is_true(sequential_s); flags = get_flags(id, local_kind, action, action, FALSE); xml_child_iter_filter( set, xml_rsc, XML_TAG_RESOURCE_REF, resource = pe_find_resource(data_set->resources, ID(xml_rsc)); resources = g_list_append(resources, resource); ); if(g_list_length(resources) == 1) { crm_err("Single set: %s", id); *rsc = resource; *end = NULL; *begin = NULL; *inv_end = NULL; *inv_begin = NULL; return TRUE; } pseudo_id = crm_concat(id, action, '-'); end_id = crm_concat(pseudo_id, "end", '-'); begin_id = crm_concat(pseudo_id, "begin", '-'); *rsc = NULL; *end = get_pseudo_op(end_id, data_set); *begin = get_pseudo_op(begin_id, data_set); + + crm_free(pseudo_id); + crm_free(begin_id); + crm_free(end_id); set_iter = resources; while(set_iter != NULL) { resource = (resource_t *) set_iter->data; set_iter = set_iter->next; key = generate_op_key(resource->id, action, 0); custom_action_order(NULL, NULL, *begin, resource, crm_strdup(key), NULL, flags|pe_order_implies_left_printed, data_set); custom_action_order(resource, crm_strdup(key), NULL, NULL, NULL, *end, flags|pe_order_implies_right_printed, data_set); if(local_kind == pe_order_kind_serialize) { /* Serialize before everything that comes after */ slist_iter( then_rsc, resource_t, set_iter, lpc, char *then_key = generate_op_key(then_rsc->id, action, 0); custom_action_order(resource, crm_strdup(key), NULL, then_rsc, then_key, NULL, flags, data_set); ); } else if(sequential) { if(last != NULL) { new_rsc_order(last, action, resource, action, flags, data_set); } last = resource; } + crm_free(key); } if(crm_is_true(symmetrical) == FALSE) { goto done; } else if(symmetrical && local_kind == pe_order_kind_serialize) { crm_config_warn("Cannot invert serialized constraint set %s", id); goto done; } else if(local_kind == pe_order_kind_serialize) { goto done; } last = NULL; action = invert_action(action); pseudo_id = crm_concat(id, action, '-'); end_id = crm_concat(pseudo_id, "end", '-'); begin_id = crm_concat(pseudo_id, "begin", '-'); *inv_end = get_pseudo_op(end_id, data_set); *inv_begin = get_pseudo_op(begin_id, data_set); + crm_free(pseudo_id); + crm_free(begin_id); + crm_free(end_id); + flags = get_flags(id, local_kind, action, action, TRUE); set_iter = resources; while(set_iter != NULL) { resource = (resource_t *) set_iter->data; set_iter = set_iter->next; key = generate_op_key(resource->id, action, 0); custom_action_order(NULL, NULL, *inv_begin, resource, crm_strdup(key), NULL, flags|pe_order_implies_left_printed, data_set); - custom_action_order(resource, crm_strdup(key), NULL, NULL, NULL, *inv_end, + custom_action_order(resource, key, NULL, NULL, NULL, *inv_end, flags|pe_order_implies_right_printed, data_set); if(sequential) { if(last != NULL) { new_rsc_order(resource, action, last, action, flags, data_set); } last = resource; } } done: g_list_free(resources); - crm_free(pseudo_id); return TRUE; } static gboolean order_rsc_sets( const char *id, xmlNode *set1, xmlNode *set2, enum pe_order_kind kind, pe_working_set_t *data_set) { resource_t *rsc_1 = NULL; resource_t *rsc_2 = NULL; const char *action_1 = crm_element_value(set1, "action"); const char *action_2 = crm_element_value(set2, "action"); const char *sequential_1 = crm_element_value(set1, "sequential"); const char *sequential_2 = crm_element_value(set2, "sequential"); enum pe_ordering flags = get_flags(id, kind, action_1, action_2, FALSE); if(crm_is_true(sequential_1)) { /* get the first one */ xml_child_iter_filter( set1, xml_rsc, XML_TAG_RESOURCE_REF, rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc)); break; ); } if(crm_is_true(sequential_2)) { /* get the last one */ const char *rid = NULL; xml_child_iter_filter( set2, xml_rsc, XML_TAG_RESOURCE_REF, rid = ID(xml_rsc); ); rsc_2 = pe_find_resource(data_set->resources, rid); } if(rsc_1 != NULL && rsc_2 != NULL) { new_rsc_order(rsc_1, action_1, rsc_2, action_2, flags, data_set); } else if(rsc_1 != NULL) { xml_child_iter_filter( set2, xml_rsc, XML_TAG_RESOURCE_REF, rsc_2 = pe_find_resource(data_set->resources, ID(xml_rsc)); new_rsc_order(rsc_1, action_1, rsc_2, action_2, flags, data_set); ); } else if(rsc_2 != NULL) { xml_child_iter_filter( set1, xml_rsc, XML_TAG_RESOURCE_REF, rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc)); new_rsc_order(rsc_1, action_1, rsc_2, action_2, flags, data_set); ); } else { xml_child_iter_filter( set1, xml_rsc, XML_TAG_RESOURCE_REF, rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc)); xml_child_iter_filter( set2, xml_rsc_2, XML_TAG_RESOURCE_REF, rsc_2 = pe_find_resource(data_set->resources, ID(xml_rsc_2)); new_rsc_order(rsc_1, action_1, rsc_2, action_2, flags, data_set); ); ); } return TRUE; } static char *null_or_opkey(resource_t *rsc, const char *action) { if(rsc == NULL) { return NULL; } return generate_op_key(rsc->id, action, 0); } gboolean unpack_rsc_order(xmlNode *xml_obj, pe_working_set_t *data_set) { gboolean any_sets = FALSE; resource_t *rsc = NULL; resource_t *last_rsc = NULL; action_t *set_end = NULL; action_t *set_begin = NULL; action_t *set_inv_end = NULL; action_t *set_inv_begin = NULL; xmlNode *last = NULL; action_t *last_end = NULL; action_t *last_begin = NULL; action_t *last_inv_end = NULL; action_t *last_inv_begin = NULL; const char *id = crm_element_value(xml_obj, XML_ATTR_ID); const char *invert = crm_element_value(xml_obj, XML_CONS_ATTR_SYMMETRICAL); enum pe_order_kind kind = get_ordering_type(xml_obj); if(invert == NULL) { invert = "true"; } xml_child_iter_filter( xml_obj, set, XML_CONS_TAG_RSC_SET, any_sets = TRUE; set = expand_idref(set, data_set->input); if(unpack_order_set(set, kind, &rsc, &set_begin, &set_end, &set_inv_begin, &set_inv_end, invert, data_set) == FALSE) { return FALSE; } else if(last) { const char *set_action = crm_element_value(set, "action"); const char *last_action = crm_element_value(last, "action"); enum pe_ordering flags = get_flags(id, kind, last_action, set_action, FALSE); if(!set_action) { set_action = RSC_START; } if(!last_action) { last_action = RSC_START; } if(rsc == NULL && last_rsc == NULL) { order_actions(last_end, set_begin, flags); } else { custom_action_order( last_rsc, null_or_opkey(last_rsc, last_action), last_end, rsc, null_or_opkey(rsc, set_action), set_begin, flags, data_set); } if(crm_is_true(invert)) { set_action = invert_action(set_action); last_action = invert_action(last_action); flags = get_flags(id, kind, last_action, set_action, TRUE); if(rsc == NULL && last_rsc == NULL) { order_actions(last_inv_begin, set_inv_end, flags); } else { custom_action_order( last_rsc, null_or_opkey(last_rsc, last_action), last_inv_begin, rsc, null_or_opkey(rsc, set_action), set_inv_end, flags, data_set); } } } else if(/* never called */last && order_rsc_sets(id, last, set, kind, data_set) == FALSE) { return FALSE; } last = set; last_rsc = rsc; last_end = set_end; last_begin = set_begin; last_inv_end = set_inv_end; last_inv_begin = set_inv_begin; ); if(any_sets == FALSE) { return unpack_simple_rsc_order(xml_obj, data_set); } return TRUE; } static gboolean unpack_colocation_set(xmlNode *set, int score, pe_working_set_t *data_set) { resource_t *with = NULL; resource_t *resource = NULL; const char *set_id = ID(set); const char *role = crm_element_value(set, "role"); const char *sequential = crm_element_value(set, "sequential"); int local_score = score; const char *score_s = crm_element_value(set, XML_RULE_ATTR_SCORE); if(score_s) { local_score = char2score(score_s); } if(sequential == NULL || crm_is_true(sequential)) { xml_child_iter_filter( set, xml_rsc, XML_TAG_RESOURCE_REF, resource = pe_find_resource(data_set->resources, ID(xml_rsc)); if(with != NULL) { crm_debug_2("Colocating %s with %s", resource->id, with->id); rsc_colocation_new(set_id, NULL, local_score, resource, with, role, role, data_set); } with = resource; ); } return TRUE; } static gboolean colocate_rsc_sets( const char *id, xmlNode *set1, xmlNode *set2, int score, pe_working_set_t *data_set) { resource_t *rsc_1 = NULL; resource_t *rsc_2 = NULL; const char *role_1 = crm_element_value(set1, "role"); const char *role_2 = crm_element_value(set2, "role"); const char *sequential_1 = crm_element_value(set1, "sequential"); const char *sequential_2 = crm_element_value(set2, "sequential"); if(crm_is_true(sequential_1)) { /* get the first one */ xml_child_iter_filter( set1, xml_rsc, XML_TAG_RESOURCE_REF, rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc)); break; ); } if(crm_is_true(sequential_2)) { /* get the last one */ const char *rid = NULL; xml_child_iter_filter( set2, xml_rsc, XML_TAG_RESOURCE_REF, rid = ID(xml_rsc); ); rsc_2 = pe_find_resource(data_set->resources, rid); } if(rsc_1 != NULL && rsc_2 != NULL) { rsc_colocation_new(id, NULL, score, rsc_1, rsc_2, role_1, role_2, data_set); } else if(rsc_1 != NULL) { xml_child_iter_filter( set2, xml_rsc, XML_TAG_RESOURCE_REF, rsc_2 = pe_find_resource(data_set->resources, ID(xml_rsc)); rsc_colocation_new(id, NULL, score, rsc_1, rsc_2, role_1, role_2, data_set); ); } else if(rsc_2 != NULL) { xml_child_iter_filter( set1, xml_rsc, XML_TAG_RESOURCE_REF, rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc)); rsc_colocation_new(id, NULL, score, rsc_1, rsc_2, role_1, role_2, data_set); ); } else { xml_child_iter_filter( set1, xml_rsc, XML_TAG_RESOURCE_REF, rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc)); xml_child_iter_filter( set2, xml_rsc_2, XML_TAG_RESOURCE_REF, rsc_2 = pe_find_resource(data_set->resources, ID(xml_rsc_2)); rsc_colocation_new(id, NULL, score, rsc_1, rsc_2, role_1, role_2, data_set); ); ); } return TRUE; } static gboolean unpack_simple_colocation(xmlNode *xml_obj, pe_working_set_t *data_set) { int score_i = 0; const char *id = crm_element_value(xml_obj, XML_ATTR_ID); const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE); const char *id_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE); const char *id_rh = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET); const char *state_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE); const char *state_rh = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET_ROLE); const char *instance_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_INSTANCE); const char *instance_rh = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET_INSTANCE); const char *attr = crm_element_value(xml_obj, XML_COLOC_ATTR_NODE_ATTR); const char *symmetrical = crm_element_value(xml_obj, XML_CONS_ATTR_SYMMETRICAL); resource_t *rsc_lh = pe_find_resource(data_set->resources, id_lh); resource_t *rsc_rh = pe_find_resource(data_set->resources, id_rh); if(rsc_lh == NULL) { crm_config_err("Invalid constraint '%s': No resource named '%s'", id, id_lh); return FALSE; } else if(rsc_rh == NULL) { crm_config_err("Invalid constraint '%s': No resource named '%s'", id, id_rh); return FALSE; } else if(instance_lh && rsc_lh->variant < pe_clone) { crm_config_err("Invalid constraint '%s': Resource '%s' is not a clone but instance %s was requested", id, id_lh, instance_lh); return FALSE; } else if(instance_rh && rsc_rh->variant < pe_clone) { crm_config_err("Invalid constraint '%s': Resource '%s' is not a clone but instance %s was requested", id, id_rh, instance_rh); return FALSE; } if(instance_lh) { rsc_lh = find_clone_instance(rsc_lh, instance_lh, data_set); if(rsc_lh == NULL) { crm_config_warn("Invalid constraint '%s': No instance '%s' of '%s'", id, instance_lh, id_lh); return FALSE; } } if(instance_rh) { rsc_rh = find_clone_instance(rsc_rh, instance_rh, data_set); if(rsc_rh == NULL) { crm_config_warn("Invalid constraint '%s': No instance '%s' of '%s'", id, instance_rh, id_rh); return FALSE; } } if(crm_is_true(symmetrical)) { crm_config_warn("The %s colocation constraint attribute has been removed." " It didn't do what you think it did anyway.", XML_CONS_ATTR_SYMMETRICAL); } if(score) { score_i = char2score(score); } rsc_colocation_new(id, attr, score_i, rsc_lh, rsc_rh, state_lh, state_rh, data_set); return TRUE; } gboolean unpack_rsc_colocation(xmlNode *xml_obj, pe_working_set_t *data_set) { int score_i = 0; xmlNode *last = NULL; gboolean any_sets = FALSE; const char *id = crm_element_value(xml_obj, XML_ATTR_ID); const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE); if(score) { score_i = char2score(score); } xml_child_iter_filter( xml_obj, set, XML_CONS_TAG_RSC_SET, any_sets = TRUE; set = expand_idref(set, data_set->input); if(unpack_colocation_set(set, score_i, data_set) == FALSE) { return FALSE; } else if(last && colocate_rsc_sets(id, last, set, score_i, data_set) == FALSE) { return FALSE; } last = set; ); if(any_sets == FALSE) { return unpack_simple_colocation(xml_obj, data_set); } return TRUE; } gboolean is_active(rsc_to_node_t *cons) { return TRUE; } diff --git a/pengine/master.c b/pengine/master.c index 587b2ed5df..86717b35ae 100644 --- a/pengine/master.c +++ b/pengine/master.c @@ -1,894 +1,898 @@ /* * 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.1 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #define VARIANT_CLONE 1 #include extern gint sort_clone_instance(gconstpointer a, gconstpointer b, gpointer data_set); extern 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) { crm_debug_4("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) { crm_debug_4("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 { crm_debug_4("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) { crm_debug_4("Ordered version (last node)"); /* global demote before first child demote */ new_rsc_order(rsc, RSC_DEMOTE, last, RSC_DEMOTE, pe_order_implies_left, data_set); } return; } /* child demote before global demoted */ new_rsc_order(child, RSC_DEMOTE, rsc, RSC_DEMOTED, pe_order_implies_right_printed, data_set); /* global demote before child demote */ new_rsc_order(rsc, RSC_DEMOTE, child, RSC_DEMOTE, pe_order_implies_left_printed, data_set); if(clone_data->ordered && last != NULL) { crm_debug_4("Ordered version"); /* child/child relative demote */ new_rsc_order(child, RSC_DEMOTE, last, RSC_DEMOTE, type, data_set); } else if(clone_data->ordered) { crm_debug_4("Ordered version (1st node)"); /* first child stop before global stopped */ new_rsc_order(child, RSC_DEMOTE, rsc, RSC_DEMOTED, type, data_set); } else { crm_debug_4("Un-ordered version"); } } static void master_update_pseudo_status( resource_t *rsc, gboolean *demoting, gboolean *promoting) { if(rsc->children) { slist_iter(child, resource_t, rsc->children, lpc, master_update_pseudo_status(child, demoting, promoting) ); return; } CRM_ASSERT(demoting != NULL); CRM_ASSERT(promoting != NULL); slist_iter( action, action_t, rsc->actions, lpc, if(*promoting && *demoting) { return; } else if(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; } ); } #define apply_master_location(list) \ slist_iter( \ cons, rsc_to_node_t, list, lpc2, \ cons_node = NULL; \ if(cons->role_filter == RSC_ROLE_MASTER) { \ crm_debug_2("Applying %s to %s", \ cons->id, child_rsc->id); \ cons_node = pe_find_node_id( \ cons->node_list_rh, chosen->details->id); \ } \ if(cons_node != NULL) { \ int new_priority = merge_weights( \ child_rsc->priority, cons_node->weight); \ crm_debug_2("\t%s: %d->%d (%d)", child_rsc->id, \ child_rsc->priority, new_priority, cons_node->weight); \ child_rsc->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; int level = LOG_DEBUG_2; #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) { slist_iter( child, resource_t, rsc->children, lpc, if(can_be_master(child) == NULL) { do_crm_log_unlikely(level, "Child %s of %s can't be promoted", child->id, rsc->id); return NULL; } ); } node = rsc->fns->location(rsc, NULL, FALSE); if(node == NULL) { do_crm_log_unlikely(level, "%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) { do_crm_log_unlikely(level, "%s cannot be master: preference: %d", rsc->id, rsc->priority); return NULL; } else if(can_run_resources(node) == FALSE) { do_crm_log_unlikely(level, "Node cant run any resources: %s", node->details->uname); return NULL; } get_clone_variant_data(clone_data, parent); local_node = pe_find_node_id( 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 { do_crm_log_unlikely(level, "%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 ) { return rc; } if(role1 > role2) { return -1; } else if(role1 < role2) { return 1; } return sort_clone_instance(a, b, data_set); } static void master_promotion_order(resource_t *rsc, pe_working_set_t *data_set) { node_t *node = NULL; node_t *chosen = NULL; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); if(clone_data->merged_master_weights) { return; } clone_data->merged_master_weights = TRUE; crm_debug_2("Merging weights for %s", rsc->id); slist_iter( child, resource_t, rsc->children, lpc, crm_debug_2("%s: %d", child->id, child->sort_index); ); dump_node_scores(LOG_DEBUG_3, rsc, "Before", rsc->allowed_nodes); slist_iter( child, resource_t, rsc->children, lpc, chosen = child->fns->location(child, NULL, FALSE); if(chosen == NULL || child->sort_index < 0) { crm_debug_3("Skipping %s", child->id); continue; } node = (node_t*)pe_find_node_id( rsc->allowed_nodes, chosen->details->id); CRM_ASSERT(node != NULL); /* adds in master preferences and rsc_location.role=Master */ node->weight = merge_weights(child->sort_index, node->weight); ); dump_node_scores(LOG_DEBUG_3, rsc, "Middle", rsc->allowed_nodes); slist_iter( constraint, rsc_colocation_t, rsc->rsc_cons, lpc, /* (re-)adds location preferences of resources that the * master instance should/must be colocated with */ if(constraint->role_lh == RSC_ROLE_MASTER) { crm_debug_2("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, constraint->score/INFINITY, constraint->score==INFINITY?FALSE:TRUE); } ); slist_iter( constraint, rsc_colocation_t, rsc->rsc_cons_lhs, lpc, /* (re-)adds location preferences of resource that wish to be * colocated with the master instance */ if(constraint->role_rh == RSC_ROLE_MASTER) { crm_debug_2("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, constraint->score/INFINITY, TRUE); } ); dump_node_scores(LOG_DEBUG_3, rsc, "After", rsc->allowed_nodes); /* write them back and sort */ slist_iter( child, resource_t, rsc->children, lpc, chosen = child->fns->location(child, NULL, FALSE); if(chosen == NULL || child->sort_index < 0) { crm_debug_2("%s: %d", child->id, child->sort_index); continue; } node = (node_t*)pe_find_node_id( rsc->allowed_nodes, chosen->details->id); CRM_ASSERT(node != NULL); child->sort_index = node->weight; crm_debug_2("%s: %d", child->id, child->sort_index); ); rsc->children = g_list_sort_with_data(rsc->children, sort_master_instance, data_set); } int master_score(resource_t *rsc, node_t *node, int not_set_value) { char *attr_name; char *name = rsc->id; const char *attr_value; int score = not_set_value, len = 0; if(rsc->children) { slist_iter( child, resource_t, rsc->children, lpc, 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(rsc->fns->state(rsc, TRUE) < RSC_ROLE_STARTED) { return score; } if(rsc->running_on) { node_t *match = pe_find_node_id(rsc->allowed_nodes, node->details->id); if(match->weight < 0) { crm_debug_2("%s on %s has score: %d - ignoring master pref", 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); crm_malloc0(attr_name, len); sprintf(attr_name, "master-%s", name); crm_debug_3("looking for %s on %s", attr_name, node->details->uname); attr_value = g_hash_table_lookup( node->details->attrs, attr_name); if(attr_value == NULL) { crm_free(attr_name); len = 8 + strlen(rsc->long_name); crm_malloc0(attr_name, len); sprintf(attr_name, "master-%s", rsc->long_name); crm_debug_3("looking for %s on %s", attr_name, node->details->uname); attr_value = g_hash_table_lookup( node->details->attrs, attr_name); } if(attr_value != NULL) { crm_debug_2("%s[%s] = %s", attr_name, node->details->uname, crm_str(attr_value)); score = char2score(attr_value); } crm_free(attr_name); return score; } #define max(a, b) aapplied_master_prefs) { /* Make sure we only do this once */ return; } clone_data->applied_master_prefs = TRUE; slist_iter( child_rsc, resource_t, rsc->children, lpc, slist_iter( node, node_t, child_rsc->allowed_nodes, lpc, 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) { crm_debug_2("\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) { crm_debug_2("\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) { 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); } slist_iter( child_rsc, resource_t, rsc->children, lpc, set_role_slave(child_rsc, current); ); } static void set_role_master(resource_t *rsc) { if(rsc->next_role == RSC_ROLE_UNKNOWN) { rsc->next_role = RSC_ROLE_MASTER; } slist_iter( child_rsc, resource_t, rsc->children, lpc, set_role_master(child_rsc); ); } node_t * master_color(resource_t *rsc, pe_working_set_t *data_set) { int promoted = 0; node_t *chosen = NULL; node_t *cons_node = NULL; enum rsc_role_e next_role = RSC_ROLE_UNKNOWN; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); apply_master_prefs(rsc); clone_color(rsc, data_set); /* count now tracks the number of masters allocated */ slist_iter(node, node_t, rsc->allowed_nodes, lpc, node->count = 0; ); /* * assign priority */ slist_iter( child_rsc, resource_t, rsc->children, lpc, GListPtr list = NULL; crm_debug_2("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 * OR target-role is set */ break; default: CRM_CHECK(FALSE/* unhandled */, crm_err("Unknown resource role: %d for %s", next_role, child_rsc->id)); } apply_master_location(child_rsc->rsc_location); apply_master_location(rsc->rsc_location); slist_iter( cons, rsc_colocation_t, child_rsc->rsc_cons, lpc2, child_rsc->cmds->rsc_colocation_lh(child_rsc, cons->rsc_rh, cons); ); child_rsc->sort_index = child_rsc->priority; crm_debug_2("Assigning priority for %s: %d", child_rsc->id, child_rsc->priority); if(next_role == RSC_ROLE_MASTER) { child_rsc->sort_index = INFINITY; } ); master_promotion_order(rsc, data_set); /* mark the first N as masters */ slist_iter( child_rsc, resource_t, rsc->children, lpc, char *score = score2char(child_rsc->sort_index); 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_unlikely(scores_log_level, "%s promotion score on %s: %s", child_rsc->id, chosen?chosen->details->uname:"none", score); } crm_free(score); chosen = NULL; /* nuke 'chosen' so that we don't promote more than the * required number of instances */ if(child_rsc->sort_index < 0) { crm_debug_2("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); } crm_debug("%s master score: %d", child_rsc->id, child_rsc->priority); if(chosen == NULL) { set_role_slave(child_rsc, FALSE); continue; } chosen->count++; crm_info("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; crm_info("%s: Promoted %d instances of a possible %d to master", rsc->id, promoted, clone_data->master_max); return NULL; } void master_create_actions(resource_t *rsc, pe_working_set_t *data_set) { action_t *action = NULL; 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); crm_debug("Creating actions for %s", rsc->id); /* create actions as normal */ clone_create_actions(rsc, data_set); slist_iter( child_rsc, resource_t, rsc->children, lpc, gboolean child_promoting = FALSE; gboolean child_demoting = FALSE; crm_debug_2("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; crm_debug_2("Created actions for %s: %d %d", child_rsc->id, child_promoting, child_demoting); ); /* promote */ action = promote_action(rsc, NULL, !any_promoting); action_complete = custom_action( rsc, promoted_key(rsc), RSC_PROMOTED, NULL, !any_promoting, TRUE, data_set); action->pseudo = TRUE; action->runnable = FALSE; action_complete->pseudo = TRUE; action_complete->runnable = FALSE; action_complete->priority = INFINITY; if(clone_data->masters_allocated > 0) { action->runnable = TRUE; action_complete->runnable = TRUE; } child_promoting_constraints(clone_data, pe_order_optional, rsc, NULL, last_promote_rsc, data_set); - clone_data->promote_notify = create_notification_boundaries( - rsc, RSC_PROMOTE, action, action_complete, 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 = demote_action(rsc, NULL, !any_demoting); action_complete = custom_action( rsc, demoted_key(rsc), RSC_DEMOTED, NULL, !any_demoting, TRUE, data_set); action_complete->priority = INFINITY; action->pseudo = TRUE; action->runnable = TRUE; action_complete->pseudo = TRUE; action_complete->runnable = TRUE; child_demoting_constraints(clone_data, pe_order_optional, rsc, NULL, last_demote_rsc, data_set); - 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); - } + 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 */ slist_iter( child_rsc, resource_t, rsc->children, lpc, child_rsc->priority = rsc->priority; ); } void master_internal_constraints(resource_t *rsc, pe_working_set_t *data_set) { 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); /* 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); slist_iter( child_rsc, resource_t, rsc->children, lpc, /* 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_list_update_one(GListPtr list, node_t *other, const char *attr, int score) { 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); slist_iter(node, node_t, list, lpc, const char *tmp = g_hash_table_lookup(node->details->attrs, attr); if(safe_str_eq(value, tmp)) { crm_debug_2("%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) { clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc_rh); CRM_CHECK(rsc_rh != NULL, return); if(is_set(rsc_rh->flags, pe_rsc_provisional)) { return; } else if(constraint->role_rh == RSC_ROLE_UNKNOWN) { crm_debug_3("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); crm_debug_2("Processing constraint %s: %d", constraint->id, constraint->score); if(constraint->role_rh == RSC_ROLE_UNKNOWN) { slist_iter( child_rsc, resource_t, rsc_rh->children, lpc, child_rsc->cmds->rsc_colocation_rh(rsc_lh, child_rsc, constraint); ); } else if(is_set(rsc_lh->flags, pe_rsc_provisional)) { GListPtr lhs = NULL, rhs = NULL; lhs = rsc_lh->allowed_nodes; slist_iter( child_rsc, resource_t, rsc_rh->children, lpc, node_t *chosen = child_rsc->fns->location(child_rsc, NULL, FALSE); enum rsc_role_e next_role = child_rsc->fns->state(child_rsc, FALSE); crm_debug_3("Processing: %s", child_rsc->id); if(chosen != NULL && next_role == constraint->role_rh) { crm_debug_3("Applying: %s %s %s %d", child_rsc->id, role2text(next_role), chosen->details->uname, constraint->score); if(constraint->score < INFINITY) { node_list_update_one(rsc_lh->allowed_nodes, chosen, constraint->node_attribute, constraint->score); } rhs = g_list_append(rhs, chosen); } ); /* Only do this if its 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 > 0) { rsc_lh->allowed_nodes = node_list_exclude(lhs, rhs, TRUE); pe_free_shallow(lhs); } } pe_free_shallow_adv(rhs, FALSE); } else if(constraint->role_lh == RSC_ROLE_MASTER) { resource_t *rh_child = find_compatible_child(rsc_lh, rsc_rh, constraint->role_rh, FALSE); if(rh_child == NULL && constraint->score >= INFINITY) { crm_debug_2("%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); crm_debug("Applying %s to %s", constraint->id, rsc_lh->id); crm_debug("\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); crm_free(name); name = crm_meta_name(XML_RSC_ATTR_MASTER_NODEMAX); crm_xml_add_int(xml, name, clone_data->master_node_max); crm_free(name); } diff --git a/pengine/native.c b/pengine/native.c index a5feee5d58..19fca5a61b 100644 --- a/pengine/native.c +++ b/pengine/native.c @@ -1,2310 +1,2313 @@ /* * 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.1 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #define DELETE_THEN_REFRESH 1 /* The crmd will remove the resource from the CIB itself, making this redundant */ #define VARIANT_NATIVE 1 #include void native_rsc_colocation_rh_must(resource_t *rsc_lh, gboolean update_lh, resource_t *rsc_rh, gboolean update_rh); void native_rsc_colocation_rh_mustnot(resource_t *rsc_lh, gboolean update_lh, resource_t *rsc_rh, gboolean update_rh); void Recurring(resource_t *rsc, action_t *start, node_t *node, pe_working_set_t *data_set); void RecurringOp(resource_t *rsc, action_t *start, node_t *node, xmlNode *operation, pe_working_set_t *data_set); void pe_post_notify( resource_t *rsc, node_t *node, action_t *op, notify_data_t *n_data, pe_working_set_t *data_set); void NoRoleChange (resource_t *rsc, node_t *current, node_t *next, pe_working_set_t *data_set); gboolean DeleteRsc (resource_t *rsc, node_t *node, gboolean optional, pe_working_set_t *data_set); gboolean StopRsc (resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set); gboolean StartRsc (resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set); gboolean DemoteRsc (resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set); gboolean PromoteRsc(resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set); gboolean RoleError (resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set); gboolean NullOp (resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set); enum rsc_role_e rsc_state_matrix[RSC_ROLE_MAX][RSC_ROLE_MAX] = { /* Current State */ /* Next State: Unknown Stopped Started Slave Master */ /* Unknown */ { RSC_ROLE_UNKNOWN, RSC_ROLE_STOPPED, RSC_ROLE_STOPPED, RSC_ROLE_STOPPED, RSC_ROLE_STOPPED, }, /* Stopped */ { RSC_ROLE_STOPPED, RSC_ROLE_STOPPED, RSC_ROLE_STARTED, RSC_ROLE_SLAVE, RSC_ROLE_SLAVE, }, /* Started */ { RSC_ROLE_STOPPED, RSC_ROLE_STOPPED, RSC_ROLE_STARTED, RSC_ROLE_SLAVE, RSC_ROLE_MASTER, }, /* Slave */ { RSC_ROLE_STOPPED, RSC_ROLE_STOPPED, RSC_ROLE_UNKNOWN, RSC_ROLE_SLAVE, RSC_ROLE_MASTER, }, /* Master */ { RSC_ROLE_STOPPED, RSC_ROLE_SLAVE, RSC_ROLE_UNKNOWN, RSC_ROLE_SLAVE, RSC_ROLE_MASTER, }, }; gboolean (*rsc_action_matrix[RSC_ROLE_MAX][RSC_ROLE_MAX])(resource_t*,node_t*,gboolean,pe_working_set_t*) = { /* Current State */ /* Next State: Unknown Stopped Started Slave Master */ /* Unknown */ { RoleError, StopRsc, RoleError, RoleError, RoleError, }, /* Stopped */ { RoleError, NullOp, StartRsc, StartRsc, RoleError, }, /* Started */ { RoleError, StopRsc, NullOp, NullOp, PromoteRsc, }, /* Slave */ { RoleError, StopRsc, RoleError, NullOp, PromoteRsc, }, /* Master */ { RoleError, RoleError, RoleError, DemoteRsc, NullOp, }, }; struct capacity_data { node_t *node; resource_t *rsc; gboolean is_enough; }; static void check_capacity(gpointer key, gpointer value, gpointer user_data) { int required = 0; int remaining = 0; struct capacity_data *data = user_data; required = crm_parse_int(value, "0"); remaining = crm_parse_int(g_hash_table_lookup(data->node->details->utilization, key), "0"); if (required > remaining) { crm_debug("Node %s has no enough %s for resource %s: required=%d remaining=%d", data->node->details->uname, (char *)key, data->rsc->id, required, remaining); data->is_enough = FALSE; } } static gboolean have_enough_capacity(node_t *node, resource_t *rsc) { struct capacity_data data; data.node = node; data.rsc = rsc; data.is_enough = TRUE; g_hash_table_foreach(rsc->utilization, check_capacity, &data); return data.is_enough; } static gboolean native_choose_node(resource_t *rsc, pe_working_set_t *data_set) { /* 1. Sort by weight 2. color.chosen_node = the node (of those with the highest wieght) with the fewest resources 3. remove color.chosen_node from all other colors */ int alloc_details = scores_log_level+1; GListPtr nodes = NULL; node_t *chosen = NULL; int lpc = 0; int multiple = 0; int length = 0; if (safe_str_neq(data_set->placement_strategy, "default")) { slist_iter( node, node_t, data_set->nodes, lpc, if (have_enough_capacity(node, rsc) == FALSE) { crm_debug("Resource %s cannot be allocated to node %s: none of enough capacity", rsc->id, node->details->uname); resource_location(rsc, node, -INFINITY, "__limit_utilization_", data_set); } ); dump_node_scores(alloc_details, rsc, "Post-utilization", rsc->allowed_nodes); } length = g_list_length(rsc->allowed_nodes); if(is_not_set(rsc->flags, pe_rsc_provisional)) { return rsc->allocated_to?TRUE:FALSE; } crm_debug_3("Choosing node for %s from %d candidates", rsc->id, length); if(rsc->allowed_nodes) { rsc->allowed_nodes = g_list_sort_with_data(rsc->allowed_nodes, sort_node_weight, data_set); nodes = rsc->allowed_nodes; chosen = g_list_nth_data(nodes, 0); if(chosen && chosen->weight > 0 && can_run_resources(chosen)) { node_t *running = g_list_nth_data(rsc->running_on, 0); if(can_run_resources(running) == FALSE) { running = NULL; } for(lpc = 1; lpc < length; lpc++) { node_t *tmp = g_list_nth_data(nodes, lpc); if(tmp->weight == chosen->weight) { multiple++; if(running && tmp->details == running->details) { /* prefer the existing node if scores are equal */ chosen = tmp; } } } } } if(multiple > 1) { int log_level = LOG_INFO; char *score = score2char(chosen->weight); if(chosen->weight >= INFINITY) { log_level = LOG_WARNING; } do_crm_log(log_level, "%d nodes with equal score (%s) for" " running %s resources. Chose %s.", multiple, score, rsc->id, chosen->details->uname); crm_free(score); } return native_assign_node(rsc, nodes, chosen, FALSE); } int node_list_attr_score(GListPtr list, const char *attr, const char *value) { int best_score = -INFINITY; const char *best_node = NULL; if(attr == NULL) { attr = "#"XML_ATTR_UNAME; } slist_iter(node, node_t, list, lpc, int weight = node->weight; if(can_run_resources(node) == FALSE) { weight = -INFINITY; } if(weight > best_score || best_node == NULL) { const char *tmp = g_hash_table_lookup(node->details->attrs, attr); if(safe_str_eq(value, tmp)) { best_score = weight; best_node = node->details->uname; } } ); if(safe_str_neq(attr, "#"XML_ATTR_UNAME)) { crm_info("Best score for %s=%s was %s with %d", attr, value, best_node?best_node:"", best_score); } return best_score; } static void node_list_update(GListPtr list1, GListPtr list2, const char *attr, int factor) { int score = 0; if(attr == NULL) { attr = "#"XML_ATTR_UNAME; } slist_iter( node, node_t, list1, lpc, CRM_CHECK(node != NULL, continue); score = node_list_attr_score(list2, attr, g_hash_table_lookup(node->details->attrs, attr)); if(factor < 0 && score < 0) { /* Negative preference for a node with a negative score * should not become a positive preference * * TODO: Decide if we want to filter only if weight == -INFINITY * */ continue; } crm_debug_2("%s: %d + %d*%d", node->details->uname, node->weight, factor, score); node->weight = merge_weights(factor*score, node->weight); ); } GListPtr native_merge_weights( resource_t *rsc, const char *rhs, GListPtr nodes, const char *attr, int factor, gboolean allow_rollback) { GListPtr work = NULL; int multiplier = 1; if(factor < 0) { multiplier = -1; } if(is_set(rsc->flags, pe_rsc_merging)) { crm_info("%s: Breaking dependency loop at %s", rhs, rsc->id); return nodes; } set_bit(rsc->flags, pe_rsc_merging); crm_debug_2("%s: Combining scores from %s", rhs, rsc->id); work = node_list_dup(nodes, FALSE, FALSE); node_list_update(work, rsc->allowed_nodes, attr, factor); if(allow_rollback && can_run_any(work) == FALSE) { crm_info("%s: Rolling back scores from %s", rhs, rsc->id); slist_destroy(node_t, n, work, crm_free(n)); clear_bit(rsc->flags, pe_rsc_merging); return nodes; } if(can_run_any(work)) { slist_iter( constraint, rsc_colocation_t, rsc->rsc_cons_lhs, lpc, crm_debug_2("Applying %s", constraint->id); work = constraint->rsc_lh->cmds->merge_weights( constraint->rsc_lh, rhs, work, constraint->node_attribute, multiplier*constraint->score/INFINITY, allow_rollback); ); } slist_destroy(node_t, n, nodes, crm_free(n)); clear_bit(rsc->flags, pe_rsc_merging); return work; } node_t * native_color(resource_t *rsc, pe_working_set_t *data_set) { int alloc_details = scores_log_level+1; if(rsc->parent && is_not_set(rsc->parent->flags, pe_rsc_allocating)) { /* never allocate children on their own */ crm_debug("Escalating allocation of %s to its parent: %s", rsc->id, rsc->parent->id); rsc->parent->cmds->color(rsc->parent, data_set); } if(is_not_set(rsc->flags, pe_rsc_provisional)) { return rsc->allocated_to; } if(is_set(rsc->flags, pe_rsc_allocating)) { crm_debug("Dependency loop detected involving %s", rsc->id); return NULL; } set_bit(rsc->flags, pe_rsc_allocating); print_resource(alloc_details, "Allocating: ", rsc, FALSE); dump_node_scores(alloc_details, rsc, "Pre-allloc", rsc->allowed_nodes); slist_iter( constraint, rsc_colocation_t, rsc->rsc_cons, lpc, GListPtr archive = NULL; resource_t *rsc_rh = constraint->rsc_rh; crm_debug_2("%s: Pre-Processing %s (%s, %d, %s)", rsc->id, constraint->id, rsc_rh->id, constraint->score, role2text(constraint->role_lh)); if(constraint->role_lh >= RSC_ROLE_MASTER || (constraint->score < 0 && constraint->score > -INFINITY)) { archive = node_list_dup(rsc->allowed_nodes, FALSE, FALSE); } rsc_rh->cmds->color(rsc_rh, data_set); rsc->cmds->rsc_colocation_lh(rsc, rsc_rh, constraint); if(archive && can_run_any(rsc->allowed_nodes) == FALSE) { crm_info("%s: Rolling back scores from %s", rsc->id, rsc_rh->id); pe_free_shallow_adv(rsc->allowed_nodes, TRUE); rsc->allowed_nodes = archive; archive = NULL; } pe_free_shallow_adv(archive, TRUE); ); dump_node_scores(alloc_details, rsc, "Post-coloc", rsc->allowed_nodes); slist_iter( constraint, rsc_colocation_t, rsc->rsc_cons_lhs, lpc, rsc->allowed_nodes = constraint->rsc_lh->cmds->merge_weights( constraint->rsc_lh, rsc->id, rsc->allowed_nodes, constraint->node_attribute, constraint->score/INFINITY, TRUE); ); print_resource(LOG_DEBUG_2, "Allocating: ", rsc, FALSE); if(rsc->next_role == RSC_ROLE_STOPPED) { crm_debug_2("Making sure %s doesn't get allocated", rsc->id); /* make sure it doesnt come up again */ resource_location( rsc, NULL, -INFINITY, XML_RSC_ATTR_TARGET_ROLE, data_set); } dump_node_scores(show_scores?0:scores_log_level, rsc, __PRETTY_FUNCTION__, rsc->allowed_nodes); if(is_set(data_set->flags, pe_flag_stonith_enabled) && is_set(data_set->flags, pe_flag_have_stonith_resource) == FALSE) { clear_bit(rsc->flags, pe_rsc_managed); } if(is_not_set(rsc->flags, pe_rsc_managed)) { const char *reason = NULL; node_t *assign_to = NULL; if(rsc->running_on == NULL) { reason = "inactive"; } else if(rsc->role == RSC_ROLE_MASTER) { assign_to = rsc->running_on->data; reason = "master"; } else if(is_set(rsc->flags, pe_rsc_failed)) { reason = "failed"; } else { assign_to = rsc->running_on->data; reason = "active"; } crm_info("Unmanaged resource %s allocated to %s: %s", rsc->id, assign_to?assign_to->details->uname:"'nowhere'", reason); native_assign_node(rsc, NULL, assign_to, TRUE); } else if(is_set(data_set->flags, pe_flag_stop_everything)) { crm_debug("Forcing %s to stop", rsc->id); native_assign_node(rsc, NULL, NULL, TRUE); } else if(is_set(rsc->flags, pe_rsc_provisional) && native_choose_node(rsc, data_set) ) { crm_debug_3("Allocated resource %s to %s", rsc->id, rsc->allocated_to->details->uname); } else if(rsc->allocated_to == NULL) { if(is_not_set(rsc->flags, pe_rsc_orphan)) { crm_info("Resource %s cannot run anywhere", rsc->id); } else if(rsc->running_on != NULL) { crm_info("Stopping orphan resource %s", rsc->id); } } else { crm_debug("Pre-Allocated resource %s to %s", rsc->id, rsc->allocated_to->details->uname); } clear_bit(rsc->flags, pe_rsc_allocating); print_resource(LOG_DEBUG_3, "Allocated ", rsc, TRUE); return rsc->allocated_to; } static gboolean is_op_dup( resource_t *rsc, const char *name, const char *interval) { gboolean dup = FALSE; const char *id = NULL; const char *value = NULL; xml_child_iter_filter( rsc->ops_xml, operation, "op", value = crm_element_value(operation, "name"); if(safe_str_neq(value, name)) { continue; } value = crm_element_value(operation, XML_LRM_ATTR_INTERVAL); if(value == NULL) { value = "0"; } if(safe_str_neq(value, interval)) { continue; } if(id == NULL) { id = ID(operation); } else { crm_config_err("Operation %s is a duplicate of %s", ID(operation), id); crm_config_err("Do not use the same (name, interval) combination more than once per resource"); dup = TRUE; } ); return dup; } void RecurringOp(resource_t *rsc, action_t *start, node_t *node, xmlNode *operation, pe_working_set_t *data_set) { char *key = NULL; const char *name = NULL; const char *value = NULL; const char *interval = NULL; const char *node_uname = NULL; unsigned long long interval_ms = 0; action_t *mon = NULL; gboolean is_optional = TRUE; GListPtr possible_matches = NULL; crm_debug_2("Creating recurring action %s for %s in role %s", ID(operation), rsc->id, role2text(rsc->next_role)); if(node != NULL) { node_uname = node->details->uname; } interval = crm_element_value(operation, XML_LRM_ATTR_INTERVAL); interval_ms = crm_get_interval(interval); if(interval_ms == 0) { return; } name = crm_element_value(operation, "name"); if(is_op_dup(rsc, name, interval)) { return; } key = generate_op_key(rsc->id, name, interval_ms); if(find_rsc_op_entry(rsc, key) == NULL) { /* disabled */ return; } if(start != NULL) { crm_debug_3("Marking %s %s due to %s", key, start->optional?"optional":"manditory", start->uuid); is_optional = start->optional; } else { crm_debug_2("Marking %s optional", key); is_optional = TRUE; } /* start a monitor for an already active resource */ possible_matches = find_actions_exact(rsc->actions, key, node); if(possible_matches == NULL) { is_optional = FALSE; crm_debug_3("Marking %s manditory: not active", key); } else { g_list_free(possible_matches); } value = crm_element_value(operation, "role"); if((rsc->next_role == RSC_ROLE_MASTER && value == NULL) || (value != NULL && text2role(value) != rsc->next_role)) { int log_level = LOG_DEBUG_2; const char *result = "Ignoring"; if(is_optional) { char *local_key = crm_strdup(key); log_level = LOG_INFO; result = "Cancelling"; /* its running : cancel it */ mon = custom_action( rsc, local_key, RSC_CANCEL, node, FALSE, TRUE, data_set); crm_free(mon->task); mon->task = crm_strdup(RSC_CANCEL); add_hash_param(mon->meta, XML_LRM_ATTR_INTERVAL, interval); add_hash_param(mon->meta, XML_LRM_ATTR_TASK, name); local_key = NULL; switch(rsc->role) { case RSC_ROLE_SLAVE: case RSC_ROLE_STARTED: if(rsc->next_role == RSC_ROLE_MASTER) { local_key = promote_key(rsc); } else if(rsc->next_role == RSC_ROLE_STOPPED) { local_key = stop_key(rsc); } break; case RSC_ROLE_MASTER: local_key = demote_key(rsc); break; default: break; } if(local_key) { custom_action_order(rsc, NULL, mon, rsc, local_key, NULL, pe_order_runnable_left, data_set); } mon = NULL; } do_crm_log(log_level, "%s action %s (%s vs. %s)", result , key, value?value:role2text(RSC_ROLE_SLAVE), role2text(rsc->next_role)); crm_free(key); key = NULL; return; } mon = custom_action(rsc, key, name, node, is_optional, TRUE, data_set); key = mon->uuid; if(is_optional) { crm_debug_2("%s\t %s (optional)", crm_str(node_uname), mon->uuid); } if(start == NULL || start->runnable == FALSE) { crm_debug("%s\t %s (cancelled : start un-runnable)", crm_str(node_uname), mon->uuid); mon->runnable = FALSE; } else if(node == NULL || node->details->online == FALSE || node->details->unclean) { crm_debug("%s\t %s (cancelled : no node available)", crm_str(node_uname), mon->uuid); mon->runnable = FALSE; } else if(mon->optional == FALSE) { crm_notice(" Start recurring %s (%llus) for %s on %s", mon->task, interval_ms/1000, rsc->id, crm_str(node_uname)); } if(rsc->next_role == RSC_ROLE_MASTER) { char *running_master = crm_itoa(EXECRA_RUNNING_MASTER); add_hash_param(mon->meta, XML_ATTR_TE_TARGET_RC, running_master); crm_free(running_master); } if(node == NULL || is_set(rsc->flags, pe_rsc_managed)) { custom_action_order(rsc, start_key(rsc), NULL, NULL, crm_strdup(key), mon, pe_order_implies_right|pe_order_runnable_left, data_set); if(rsc->next_role == RSC_ROLE_MASTER) { custom_action_order( rsc, promote_key(rsc), NULL, rsc, NULL, mon, pe_order_optional|pe_order_runnable_left, data_set); } else if(rsc->role == RSC_ROLE_MASTER) { custom_action_order( rsc, demote_key(rsc), NULL, rsc, NULL, mon, pe_order_optional|pe_order_runnable_left, data_set); } } } void Recurring(resource_t *rsc, action_t *start, node_t *node, pe_working_set_t *data_set) { if(is_not_set(data_set->flags, pe_flag_maintenance_mode)) { xml_child_iter_filter( rsc->ops_xml, operation, "op", RecurringOp(rsc, start, node, operation, data_set); ); } } void native_create_actions(resource_t *rsc, pe_working_set_t *data_set) { action_t *start = NULL; node_t *chosen = NULL; enum rsc_role_e role = RSC_ROLE_UNKNOWN; enum rsc_role_e next_role = RSC_ROLE_UNKNOWN; crm_debug_2("Createing actions for %s: %s->%s", rsc->id, role2text(rsc->role), role2text(rsc->next_role)); chosen = rsc->allocated_to; if(chosen != NULL && rsc->next_role == RSC_ROLE_UNKNOWN) { rsc->next_role = RSC_ROLE_STARTED; } else if(rsc->next_role == RSC_ROLE_UNKNOWN) { rsc->next_role = RSC_ROLE_STOPPED; } get_rsc_attributes(rsc->parameters, rsc, chosen, data_set); if(g_list_length(rsc->running_on) > 1) { if(rsc->recovery_type == recovery_stop_start) { pe_proc_warn("Attempting recovery of resource %s", rsc->id); if(rsc->role == RSC_ROLE_MASTER) { DemoteRsc(rsc, NULL, FALSE, data_set); } StopRsc(rsc, NULL, FALSE, data_set); rsc->role = RSC_ROLE_STOPPED; } } else if(rsc->running_on != NULL) { node_t *current = rsc->running_on->data; NoRoleChange(rsc, current, chosen, data_set); } else if(rsc->role == RSC_ROLE_STOPPED && rsc->next_role == RSC_ROLE_STOPPED) { char *key = start_key(rsc); GListPtr possible_matches = find_actions(rsc->actions, key, NULL); slist_iter( action, action_t, possible_matches, lpc, action->optional = TRUE; /* action->pseudo = TRUE; */ ); g_list_free(possible_matches); crm_debug_2("Stopping a stopped resource"); crm_free(key); goto do_recurring; } else if(rsc->role != RSC_ROLE_STOPPED) { /* A cheap trick to account for the fact that Master/Slave groups may not be * completely running when we set their role to Slave */ crm_debug_2("Resetting %s.role = %s (was %s)", rsc->id, role2text(RSC_ROLE_STOPPED), role2text(rsc->role)); rsc->role = RSC_ROLE_STOPPED; } role = rsc->role; while(role != rsc->next_role) { next_role = rsc_state_matrix[role][rsc->next_role]; crm_debug_2("Executing: %s->%s (%s)", role2text(role), role2text(next_role), rsc->id); if(rsc_action_matrix[role][next_role]( rsc, chosen, FALSE, data_set) == FALSE) { break; } role = next_role; } do_recurring: if(rsc->next_role != RSC_ROLE_STOPPED || is_set(rsc->flags, pe_rsc_managed) == FALSE) { start = start_action(rsc, chosen, TRUE); Recurring(rsc, start, chosen, data_set); } } void native_internal_constraints(resource_t *rsc, pe_working_set_t *data_set) { int type = pe_order_optional; const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); action_t *all_stopped = get_pseudo_op(ALL_STOPPED, data_set); if(rsc->variant == pe_native) { type |= pe_order_implies_right; } if(rsc->parent == NULL || rsc->parent->variant == pe_group) { type |= pe_order_restart; } new_rsc_order(rsc, RSC_STOP, rsc, RSC_START, type, data_set); new_rsc_order(rsc, RSC_DEMOTE, rsc, RSC_STOP, pe_order_demote_stop, data_set); new_rsc_order(rsc, RSC_START, rsc, RSC_PROMOTE, pe_order_runnable_left, data_set); new_rsc_order(rsc, RSC_DELETE, rsc, RSC_START, pe_order_optional, data_set); if(is_not_set(rsc->flags, pe_rsc_managed)) { crm_debug_3("Skipping fencing constraints for unmanaged resource: %s", rsc->id); return; } if(rsc->variant == pe_native && safe_str_neq(class, "stonith")) { custom_action_order( rsc, stop_key(rsc), NULL, NULL, crm_strdup(all_stopped->task), all_stopped, pe_order_implies_right|pe_order_runnable_left, data_set); } if (safe_str_neq(data_set->placement_strategy, "default") && g_hash_table_size(rsc->utilization) > 0) { slist_iter( current, node_t, rsc->running_on, lpc, char *load_stopped_task = crm_concat(LOAD_STOPPED, current->details->uname, '_'); action_t *load_stopped = get_pseudo_op(load_stopped_task, data_set); load_stopped->node = current; load_stopped->optional = FALSE; custom_action_order( rsc, stop_key(rsc), NULL, NULL, load_stopped_task, load_stopped, pe_order_optional, data_set); ); slist_iter( next, node_t, rsc->allowed_nodes, lpc, char *load_stopped_task = crm_concat(LOAD_STOPPED, next->details->uname, '_'); action_t *load_stopped = get_pseudo_op(load_stopped_task, data_set); load_stopped->node = next; load_stopped->optional = FALSE; custom_action_order( NULL, load_stopped_task, load_stopped, rsc, start_key(rsc), NULL, pe_order_optional, data_set); ); } } void native_rsc_colocation_lh( resource_t *rsc_lh, resource_t *rsc_rh, rsc_colocation_t *constraint) { if(rsc_lh == NULL) { pe_err("rsc_lh was NULL for %s", constraint->id); return; } else if(constraint->rsc_rh == NULL) { pe_err("rsc_rh was NULL for %s", constraint->id); return; } crm_debug_2("Processing colocation constraint between %s and %s", rsc_lh->id, rsc_rh->id); rsc_rh->cmds->rsc_colocation_rh(rsc_lh, rsc_rh, constraint); } static gboolean filter_colocation_constraint( resource_t *rsc_lh, resource_t *rsc_rh, rsc_colocation_t *constraint) { int level = LOG_DEBUG_4; if(constraint->score == 0){ return FALSE; } if(constraint->score > 0 && constraint->role_lh != RSC_ROLE_UNKNOWN && constraint->role_lh != rsc_lh->next_role) { do_crm_log_unlikely(level, "LH: Skipping constraint: \"%s\" state filter", role2text(constraint->role_rh)); return FALSE; } if(constraint->score > 0 && constraint->role_rh != RSC_ROLE_UNKNOWN && constraint->role_rh != rsc_rh->next_role) { do_crm_log_unlikely(level, "RH: Skipping constraint: \"%s\" state filter", role2text(constraint->role_rh)); return FALSE; } if(constraint->score < 0 && constraint->role_lh != RSC_ROLE_UNKNOWN && constraint->role_lh == rsc_lh->next_role) { do_crm_log_unlikely(level, "LH: Skipping -ve constraint: \"%s\" state filter", role2text(constraint->role_rh)); return FALSE; } if(constraint->score < 0 && constraint->role_rh != RSC_ROLE_UNKNOWN && constraint->role_rh == rsc_rh->next_role) { do_crm_log_unlikely(level, "RH: Skipping -ve constraint: \"%s\" state filter", role2text(constraint->role_rh)); return FALSE; } return TRUE; } static void colocation_match( resource_t *rsc_lh, resource_t *rsc_rh, rsc_colocation_t *constraint) { const char *tmp = NULL; const char *value = NULL; const char *attribute = "#id"; - GListPtr archive = NULL; + GListPtr work = NULL; gboolean do_check = FALSE; if(constraint->node_attribute != NULL) { attribute = constraint->node_attribute; } if(rsc_rh->allocated_to) { value = g_hash_table_lookup( rsc_rh->allocated_to->details->attrs, attribute); do_check = TRUE; } else if(constraint->score < 0) { /* nothing to do: * anti-colocation with something thats not running */ return; } - if(constraint->score > -INFINITY && constraint->score < INFINITY) { - archive = node_list_dup(rsc_lh->allowed_nodes, FALSE, FALSE); - } + work = node_list_dup(rsc_lh->allowed_nodes, FALSE, FALSE); slist_iter( - node, node_t, rsc_lh->allowed_nodes, lpc, + node, node_t, work, lpc, tmp = g_hash_table_lookup(node->details->attrs, attribute); if(do_check && safe_str_eq(tmp, value)) { if(constraint->score < INFINITY) { crm_debug_2("%s: %s.%s += %d", constraint->id, rsc_lh->id, node->details->uname, constraint->score); node->weight = merge_weights( constraint->score, node->weight); } } else if(do_check == FALSE || constraint->score >= INFINITY) { crm_debug_2("%s: %s.%s -= %d (%s)", constraint->id, rsc_lh->id, node->details->uname, constraint->score, do_check?"failed":"unallocated"); node->weight = merge_weights(-constraint->score, node->weight); } ); - if(can_run_any(rsc_lh->allowed_nodes) == FALSE) { - if(archive) { - char *score = score2char(constraint->score); - crm_info("%s: Rolling back scores from %s (%d, %s)", - rsc_lh->id, rsc_rh->id, do_check, score); - pe_free_shallow_adv(rsc_lh->allowed_nodes, TRUE); - rsc_lh->allowed_nodes = archive; - crm_free(score); - } + if(can_run_any(work) + || constraint->score <= -INFINITY + || constraint->score >= INFINITY) { + slist_destroy(node_t, node, rsc_lh->allowed_nodes, crm_free(node)); + rsc_lh->allowed_nodes = work; + work = NULL; + + } else { + char *score = score2char(constraint->score); + crm_info("%s: Rolling back scores from %s (%d, %s)", + rsc_lh->id, rsc_rh->id, do_check, score); + crm_free(score); } + + slist_destroy(node_t, node, work, crm_free(node)); } void native_rsc_colocation_rh( resource_t *rsc_lh, resource_t *rsc_rh, rsc_colocation_t *constraint) { crm_debug_2("%sColocating %s with %s (%s, weight=%d)", constraint->score >= 0?"":"Anti-", rsc_lh->id, rsc_rh->id, constraint->id, constraint->score); if(filter_colocation_constraint(rsc_lh, rsc_rh, constraint) == FALSE) { return; } if(is_set(rsc_rh->flags, pe_rsc_provisional)) { return; } else if(is_not_set(rsc_lh->flags, pe_rsc_provisional)) { /* error check */ struct node_shared_s *details_lh; struct node_shared_s *details_rh; if((constraint->score > -INFINITY) && (constraint->score < INFINITY)) { return; } details_rh = rsc_rh->allocated_to?rsc_rh->allocated_to->details:NULL; details_lh = rsc_lh->allocated_to?rsc_lh->allocated_to->details:NULL; if(constraint->score == INFINITY && details_lh != details_rh) { crm_err("%s and %s are both allocated" " but to different nodes: %s vs. %s", rsc_lh->id, rsc_rh->id, details_lh?details_lh->uname:"n/a", details_rh?details_rh->uname:"n/a"); } else if(constraint->score == -INFINITY && details_lh == details_rh) { crm_err("%s and %s are both allocated" " but to the SAME node: %s", rsc_lh->id, rsc_rh->id, details_rh?details_rh->uname:"n/a"); } return; } else { colocation_match(rsc_lh, rsc_rh, constraint); } } static GListPtr find_actions_by_task(GListPtr actions, resource_t *rsc, const char *original_key) { GListPtr list = NULL; list = find_actions(actions, original_key, NULL); if(list == NULL) { /* we're potentially searching a child of the original resource */ char *key = NULL; char *tmp = NULL; char *task = NULL; int interval = 0; if(parse_op_key(original_key, &tmp, &task, &interval)) { key = generate_op_key(rsc->id, task, interval); /* crm_err("looking up %s instead of %s", key, original_key); */ /* slist_iter(action, action_t, actions, lpc, */ /* crm_err(" - %s", action->uuid)); */ list = find_actions(actions, key, NULL); } else { crm_err("search key: %s", original_key); } crm_free(key); crm_free(tmp); crm_free(task); } return list; } void native_rsc_order_lh(resource_t *lh_rsc, order_constraint_t *order, pe_working_set_t *data_set) { GListPtr lh_actions = NULL; action_t *lh_action = order->lh_action; resource_t *rh_rsc = order->rh_rsc; crm_debug_3("Processing LH of ordering constraint %d", order->id); CRM_ASSERT(lh_rsc != NULL); if(lh_action != NULL) { lh_actions = g_list_append(NULL, lh_action); } else if(lh_action == NULL) { lh_actions = find_actions_by_task( lh_rsc->actions, lh_rsc, order->lh_action_task); } if(lh_actions == NULL && lh_rsc != rh_rsc) { char *key = NULL; char *rsc_id = NULL; char *op_type = NULL; int interval = 0; crm_debug_4("No LH-Side (%s/%s) found for constraint %d with %s - creating", lh_rsc->id, order->lh_action_task, order->id, order->rh_action_task); parse_op_key( order->lh_action_task, &rsc_id, &op_type, &interval); key = generate_op_key(lh_rsc->id, op_type, interval); lh_action = custom_action(lh_rsc, key, op_type, NULL, TRUE, TRUE, data_set); if(lh_rsc->fns->state(lh_rsc, TRUE) == RSC_ROLE_STOPPED && safe_str_eq(op_type, RSC_STOP)) { lh_action->pseudo = TRUE; lh_action->runnable = TRUE; } lh_actions = g_list_append(NULL, lh_action); crm_free(op_type); crm_free(rsc_id); } slist_iter( lh_action_iter, action_t, lh_actions, lpc, if(rh_rsc == NULL && order->rh_action) { rh_rsc = order->rh_action->rsc; } if(rh_rsc) { rh_rsc->cmds->rsc_order_rh( lh_action_iter, rh_rsc, order); } else if(order->rh_action) { order_actions( lh_action_iter, order->rh_action, order->type); } ); pe_free_shallow_adv(lh_actions, FALSE); } void native_rsc_order_rh( action_t *lh_action, resource_t *rsc, order_constraint_t *order) { GListPtr rh_actions = NULL; action_t *rh_action = NULL; CRM_CHECK(rsc != NULL, return); CRM_CHECK(order != NULL, return); rh_action = order->rh_action; crm_debug_3("Processing RH of ordering constraint %d", order->id); if(rh_action != NULL) { rh_actions = g_list_append(NULL, rh_action); } else if(rsc != NULL) { rh_actions = find_actions_by_task( rsc->actions, rsc, order->rh_action_task); } if(rh_actions == NULL) { crm_debug_4("No RH-Side (%s/%s) found for constraint..." " ignoring", rsc->id,order->rh_action_task); if(lh_action) { crm_debug_4("LH-Side was: %s", lh_action->uuid); } return; } slist_iter( rh_action_iter, action_t, rh_actions, lpc, if(lh_action) { order_actions(lh_action, rh_action_iter, order->type); } else if(order->type & pe_order_implies_right) { rh_action_iter->runnable = FALSE; crm_warn("Unrunnable %s 0x%.6x", rh_action_iter->uuid, order->type); } else { crm_warn("neither %s 0x%.6x", rh_action_iter->uuid, order->type); } ); pe_free_shallow_adv(rh_actions, FALSE); } void native_rsc_location(resource_t *rsc, rsc_to_node_t *constraint) { GListPtr or_list; crm_debug_2("Applying %s (%s) to %s", constraint->id, role2text(constraint->role_filter), rsc->id); /* take "lifetime" into account */ if(constraint == NULL) { pe_err("Constraint is NULL"); return; } else if(rsc == NULL) { pe_err("LHS of rsc_to_node (%s) is NULL", constraint->id); return; } else if(constraint->role_filter > 0 && constraint->role_filter != rsc->next_role) { crm_debug("Constraint (%s) is not active (role : %s)", constraint->id, role2text(constraint->role_filter)); return; } else if(is_active(constraint) == FALSE) { crm_debug_2("Constraint (%s) is not active", constraint->id); return; } if(constraint->node_list_rh == NULL) { crm_debug_2("RHS of constraint %s is NULL", constraint->id); return; } or_list = node_list_or( rsc->allowed_nodes, constraint->node_list_rh, FALSE); pe_free_shallow(rsc->allowed_nodes); rsc->allowed_nodes = or_list; slist_iter(node, node_t, or_list, lpc, crm_debug_3("%s + %s : %d", rsc->id, node->details->uname, node->weight); ); } void native_expand(resource_t *rsc, pe_working_set_t *data_set) { crm_debug_3("Processing actions from %s", rsc->id); slist_iter( action, action_t, rsc->actions, lpc, crm_debug_4("processing action %d for rsc=%s", action->id, rsc->id); graph_element_from_action(action, data_set); ); slist_iter( child_rsc, resource_t, rsc->children, lpc, child_rsc->cmds->expand(child_rsc, data_set); ); } void LogActions(resource_t *rsc, pe_working_set_t *data_set) { node_t *next = NULL; node_t *current = NULL; gboolean moving = FALSE; if(rsc->children) { slist_iter( child_rsc, resource_t, rsc->children, lpc, LogActions(child_rsc, data_set); ); return; } next = rsc->allocated_to; if(rsc->running_on) { current = rsc->running_on->data; if(rsc->role == RSC_ROLE_STOPPED) { /* * This can occur when resources are being recovered * We fiddle with the current role in native_create_actions() */ rsc->role = RSC_ROLE_STARTED; } } if(current == NULL && is_set(rsc->flags, pe_rsc_orphan)) { /* Don't log stopped orphans */ return; } if(is_not_set(rsc->flags, pe_rsc_managed) || (current == NULL && next == NULL)) { crm_notice("Leave resource %s\t(%s%s)", rsc->id, role2text(rsc->role), is_not_set(rsc->flags, pe_rsc_managed)?" unmanaged":""); return; } if(current != NULL && next != NULL && safe_str_neq(current->details->id, next->details->id)) { moving = TRUE; } if(rsc->role == rsc->next_role) { action_t *start = NULL; char *key = start_key(rsc); GListPtr possible_matches = find_actions(rsc->actions, key, next); crm_free(key); if(possible_matches) { start = possible_matches->data; g_list_free(possible_matches); } key = generate_op_key(rsc->id, CRMD_ACTION_MIGRATED, 0); possible_matches = find_actions(rsc->actions, key, next); crm_free(key); CRM_CHECK(next != NULL,); if(next == NULL) { } else if(possible_matches) { crm_notice("Migrate resource %s\t(%s %s -> %s)", rsc->id, role2text(rsc->role), current->details->uname, next->details->uname); g_list_free(possible_matches); } else if(start == NULL || start->optional) { crm_notice("Leave resource %s\t(%s %s)", rsc->id, role2text(rsc->role), next->details->uname); } else if(moving && current) { crm_notice("Move resource %s\t(%s %s -> %s)", rsc->id, role2text(rsc->role), current->details->uname, next->details->uname); } else if(is_set(rsc->flags, pe_rsc_failed)) { crm_notice("Recover resource %s\t(%s %s)", rsc->id, role2text(rsc->role), next->details->uname); } else if(start && start->runnable == FALSE) { crm_notice("Stop resource %s\t(%s %s)", rsc->id, role2text(rsc->role), next->details->uname); } else { crm_notice("Restart resource %s\t(%s %s)", rsc->id, role2text(rsc->role), next->details->uname); } return; } if(rsc->role > RSC_ROLE_SLAVE && rsc->role > rsc->next_role) { CRM_CHECK(current != NULL,); if(current != NULL) { crm_notice("Demote %s\t(%s -> %s %s)", rsc->id, role2text(rsc->role), role2text(rsc->next_role), current->details->uname); } } if(rsc->next_role == RSC_ROLE_STOPPED || moving) { CRM_CHECK(current != NULL,); slist_iter(node, node_t, rsc->running_on, lpc, crm_notice("Stop resource %s\t(%s)", rsc->id, node->details->uname)); } if(rsc->role == RSC_ROLE_STOPPED || moving) { CRM_CHECK(next != NULL,); if(next != NULL) { crm_notice("Start %s\t(%s)", rsc->id, next->details->uname); } } if(rsc->next_role > RSC_ROLE_SLAVE && rsc->role < rsc->next_role) { CRM_CHECK(next != NULL,); crm_notice("Promote %s\t(%s -> %s %s)", rsc->id, role2text(rsc->role), role2text(rsc->next_role), next->details->uname); } } void NoRoleChange(resource_t *rsc, node_t *current, node_t *next, pe_working_set_t *data_set) { action_t *stop = NULL; action_t *start = NULL; GListPtr possible_matches = NULL; crm_debug_2("Executing: %s (role=%s)", rsc->id, role2text(rsc->next_role)); if(current == NULL || next == NULL) { return; } if(is_set(rsc->flags, pe_rsc_failed) || safe_str_neq(current->details->id, next->details->id)) { if(rsc->next_role > RSC_ROLE_STARTED) { gboolean optional = TRUE; if(rsc->role == RSC_ROLE_MASTER) { optional = FALSE; } DemoteRsc(rsc, current, optional, data_set); } if(rsc->role == RSC_ROLE_MASTER) { DemoteRsc(rsc, current, FALSE, data_set); } StopRsc(rsc, current, FALSE, data_set); StartRsc(rsc, next, FALSE, data_set); if(rsc->next_role == RSC_ROLE_MASTER) { PromoteRsc(rsc, next, FALSE, data_set); } possible_matches = find_recurring_actions(rsc->actions, next); slist_iter(match, action_t, possible_matches, lpc, if(match->optional == FALSE) { crm_debug("Fixing recurring action: %s", match->uuid); match->optional = TRUE; } ); g_list_free(possible_matches); } else if(is_set(rsc->flags, pe_rsc_start_pending)) { start = start_action(rsc, next, TRUE); if(start->runnable) { /* wait for StartRsc() to be called */ rsc->role = RSC_ROLE_STOPPED; } else { /* wait for StopRsc() to be called */ rsc->next_role = RSC_ROLE_STOPPED; } } else { stop = stop_action(rsc, current, TRUE); start = start_action(rsc, next, TRUE); stop->optional = start->optional; if(rsc->next_role > RSC_ROLE_STARTED) { DemoteRsc(rsc, current, start->optional, data_set); } StopRsc(rsc, current, start->optional, data_set); StartRsc(rsc, current, start->optional, data_set); if(rsc->next_role == RSC_ROLE_MASTER) { PromoteRsc(rsc, next, start->optional, data_set); } if(start->runnable == FALSE) { rsc->next_role = RSC_ROLE_STOPPED; } } } gboolean StopRsc(resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set) { action_t *stop = NULL; const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); crm_debug_2("Executing: %s", rsc->id); if(rsc->next_role == RSC_ROLE_STOPPED && rsc->variant == pe_native && safe_str_eq(class, "stonith")) { action_t *all_stopped = get_pseudo_op(ALL_STOPPED, data_set); custom_action_order( NULL, crm_strdup(all_stopped->task), all_stopped, rsc, stop_key(rsc), NULL, pe_order_implies_left|pe_order_stonith_stop, data_set); } slist_iter( current, node_t, rsc->running_on, lpc, stop = stop_action(rsc, current, optional); if(is_set(data_set->flags, pe_flag_remove_after_stop)) { DeleteRsc(rsc, current, optional, data_set); } ); return TRUE; } gboolean StartRsc(resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set) { action_t *start = NULL; crm_debug_2("Executing: %s", rsc->id); start = start_action(rsc, next, TRUE); if(start->runnable && optional == FALSE) { start->optional = FALSE; } return TRUE; } gboolean PromoteRsc(resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set) { char *key = NULL; gboolean runnable = TRUE; GListPtr action_list = NULL; crm_debug_2("Executing: %s", rsc->id); CRM_CHECK(rsc->next_role == RSC_ROLE_MASTER, crm_err("Next role: %s", role2text(rsc->next_role)); return FALSE); CRM_CHECK(next != NULL, return FALSE); key = start_key(rsc); action_list = find_actions_exact(rsc->actions, key, next); crm_free(key); slist_iter(start, action_t, action_list, lpc, if(start->runnable == FALSE) { runnable = FALSE; } ); g_list_free(action_list); if(runnable) { promote_action(rsc, next, optional); return TRUE; } crm_debug("%s\tPromote %s (canceled)", next->details->uname, rsc->id); key = promote_key(rsc); action_list = find_actions_exact(rsc->actions, key, next); crm_free(key); slist_iter(promote, action_t, action_list, lpc, promote->runnable = FALSE; ); g_list_free(action_list); return TRUE; } gboolean DemoteRsc(resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set) { crm_debug_2("Executing: %s", rsc->id); /* CRM_CHECK(rsc->next_role == RSC_ROLE_SLAVE, return FALSE); */ slist_iter( current, node_t, rsc->running_on, lpc, demote_action(rsc, current, optional); ); return TRUE; } gboolean RoleError(resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set) { crm_debug("Executing: %s", rsc->id); CRM_CHECK(FALSE, return FALSE); return FALSE; } gboolean NullOp(resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set) { crm_debug_2("Executing: %s", rsc->id); return FALSE; } gboolean DeleteRsc(resource_t *rsc, node_t *node, gboolean optional, pe_working_set_t *data_set) { action_t *delete = NULL; #if DELETE_THEN_REFRESH action_t *refresh = NULL; #endif if(is_set(rsc->flags, pe_rsc_failed)) { crm_debug_2("Resource %s not deleted from %s: failed", rsc->id, node->details->uname); return FALSE; } else if(node == NULL) { crm_debug_2("Resource %s not deleted: NULL node", rsc->id); return FALSE; } else if(node->details->unclean || node->details->online == FALSE) { crm_debug_2("Resource %s not deleted from %s: unrunnable", rsc->id, node->details->uname); return FALSE; } crm_notice("Removing %s from %s", rsc->id, node->details->uname); delete = delete_action(rsc, node, optional); new_rsc_order(rsc, RSC_STOP, rsc, RSC_DELETE, optional?pe_order_implies_right:pe_order_implies_left, data_set); #if DELETE_THEN_REFRESH refresh = custom_action( NULL, crm_strdup(CRM_OP_LRM_REFRESH), CRM_OP_LRM_REFRESH, node, FALSE, TRUE, data_set); add_hash_param(refresh->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE); order_actions(delete, refresh, pe_order_optional); #endif return TRUE; } #include <../lib/pengine/unpack.h> gboolean native_create_probe(resource_t *rsc, node_t *node, action_t *complete, gboolean force, pe_working_set_t *data_set) { char *key = NULL; action_t *probe = NULL; node_t *running = NULL; resource_t *top = uber_parent(rsc); static const char *rc_master = NULL; static const char *rc_inactive = NULL; if(rc_inactive == NULL) { rc_inactive = crm_itoa(EXECRA_NOT_RUNNING); rc_master = crm_itoa(EXECRA_RUNNING_MASTER); } CRM_CHECK(node != NULL, return FALSE); if(force == FALSE && is_not_set(data_set->flags, pe_flag_startup_probes)) { crm_debug_2("Skipping active resource detection for %s", rsc->id); return FALSE; } if(rsc->children) { gboolean any_created = FALSE; slist_iter( child_rsc, resource_t, rsc->children, lpc, any_created = child_rsc->cmds->create_probe( child_rsc, node, complete, force, data_set) || any_created; ); return any_created; } if(is_set(rsc->flags, pe_rsc_orphan)) { crm_debug_2("Skipping orphan: %s", rsc->id); return FALSE; } running = pe_find_node_id(rsc->known_on, node->details->id); if(force == FALSE && running != NULL) { /* we already know the status of the resource on this node */ crm_debug_3("Skipping active: %s", rsc->id); return FALSE; } if(running == NULL && is_set(top->flags, pe_rsc_unique) == FALSE) { /* Annoyingly we also need to check any other clone instances * Clumsy, but it will work. * * An alternative would be to update known_on for every peer * during process_rsc_state() * * This code desperately needs optimization * ptest -x with 100 nodes, 100 clones and clone-max=10: * No probes O(25s) * Detection without clone loop O(3m) * Detection with clone loop O(8m) ptest[32211]: 2010/02/18_14:27:55 CRIT: stage5: Probing for unknown resources ptest[32211]: 2010/02/18_14:33:39 CRIT: stage5: Done ptest[32211]: 2010/02/18_14:35:05 CRIT: stage7: Updating action states ptest[32211]: 2010/02/18_14:35:05 CRIT: stage7: Done */ char *clone_id = clone_zero(rsc->id); resource_t *peer = pe_find_resource(top->children, clone_id); while(peer && running == NULL) { running = pe_find_node_id(peer->known_on, node->details->id); if(force == FALSE && running != NULL) { /* we already know the status of the resource on this node */ crm_debug_3("Skipping active clone: %s", rsc->id); crm_free(clone_id); return FALSE; } clone_id = increment_clone(clone_id); peer = pe_find_resource(data_set->resources, clone_id); } crm_free(clone_id); } key = generate_op_key(rsc->id, RSC_STATUS, 0); probe = custom_action(rsc, key, RSC_STATUS, node, FALSE, TRUE, data_set); probe->optional = FALSE; if(running == NULL) { add_hash_param(probe->meta, XML_ATTR_TE_TARGET_RC, rc_inactive); } else if(rsc->role == RSC_ROLE_MASTER) { add_hash_param(probe->meta, XML_ATTR_TE_TARGET_RC, rc_master); } crm_debug("Probing %s on %s (%s)", rsc->id, node->details->uname, role2text(rsc->role)); order_actions(probe, complete, pe_order_implies_right); return TRUE; } static void native_start_constraints( resource_t *rsc, action_t *stonith_op, gboolean is_stonith, pe_working_set_t *data_set) { node_t *target = stonith_op?stonith_op->node:NULL; if(is_stonith) { char *key = start_key(rsc); action_t *ready = get_pseudo_op(STONITH_UP, data_set); crm_debug_2("Ordering %s action before stonith events", key); custom_action_order( rsc, key, NULL, NULL, crm_strdup(ready->task), ready, pe_order_optional, data_set); } else { action_t *all_stopped = get_pseudo_op(ALL_STOPPED, data_set); action_t *stonith_done = get_pseudo_op(STONITH_DONE, data_set); slist_iter(action, action_t, rsc->actions, lpc2, if(action->needs == rsc_req_stonith) { order_actions(stonith_done, action, pe_order_implies_left); } else if(target != NULL && safe_str_eq(action->task, RSC_START) && NULL == pe_find_node_id( rsc->known_on, target->details->id)) { /* if known == NULL, then we dont know if * the resource is active on the node * we're about to shoot * * in this case, regardless of action->needs, * the only safe option is to wait until * the node is shot before doing anything * to with the resource * * its analogous to waiting for all the probes * for rscX to complete before starting rscX * * the most likely explaination is that the * DC died and took its status with it */ crm_debug("Ordering %s after %s recovery", action->uuid, target->details->uname); order_actions(all_stopped, action, pe_order_implies_left|pe_order_runnable_left); } ); } } static void native_stop_constraints( resource_t *rsc, action_t *stonith_op, gboolean is_stonith, pe_working_set_t *data_set) { char *key = NULL; GListPtr action_list = NULL; resource_t *top = uber_parent(rsc); key = stop_key(rsc); action_list = find_actions(rsc->actions, key, stonith_op->node); crm_free(key); /* add the stonith OP as a stop pre-req and the mark the stop * as a pseudo op - since its now redundant */ slist_iter( action, action_t, action_list, lpc2, resource_t *parent = NULL; if(action->node->details->online && action->node->details->unclean == FALSE && is_set(rsc->flags, pe_rsc_failed)) { continue; } if(is_set(rsc->flags, pe_rsc_failed)) { crm_warn("Stop of failed resource %s is" " implicit after %s is fenced", rsc->id, action->node->details->uname); } else { crm_info("%s is implicit after %s is fenced", action->uuid, action->node->details->uname); } /* the stop would never complete and is * now implied by the stonith operation */ action->pseudo = TRUE; action->runnable = TRUE; action->implied_by_stonith = TRUE; if(is_stonith == FALSE) { action_t *parent_stop = find_first_action(top->actions, NULL, RSC_STOP, NULL); order_actions(stonith_op, action, pe_order_optional); order_actions(stonith_op, parent_stop, pe_order_optional); } if(is_set(rsc->flags, pe_rsc_notify)) { /* Create a second notification that will be delivered * immediately after the node is fenced * * Basic problem: * - C is a clone active on the node to be shot and stopping on another * - R is a resource that depends on C * * + C.stop depends on R.stop * + C.stopped depends on STONITH * + C.notify depends on C.stopped * + C.healthy depends on C.notify * + R.stop depends on C.healthy * * The extra notification here changes * + C.healthy depends on C.notify * into: * + C.healthy depends on C.notify' * + C.notify' depends on STONITH' * thus breaking the loop */ notify_data_t *n_data = create_notification_boundaries(rsc, RSC_STOP, NULL, stonith_op, data_set); crm_info("Creating secondary notification for %s", action->uuid); collect_notification_data(rsc, TRUE, FALSE, n_data); g_hash_table_insert(n_data->keys, crm_strdup("notify_stop_resource"), crm_strdup(rsc->id)); g_hash_table_insert(n_data->keys, crm_strdup("notify_stop_uname"), crm_strdup(action->node->details->uname)); create_notifications(uber_parent(rsc), n_data, data_set); free_notification_data(n_data); } /* find the top-most resource */ parent = rsc->parent; while(parent != NULL && parent->parent != NULL) { parent = parent->parent; } if(parent) { crm_debug_2("Re-creating actions for %s", parent->id); parent->cmds->create_actions(parent, data_set); /* make sure we dont mess anything up in create_actions */ CRM_CHECK(action->pseudo, action->pseudo = TRUE); CRM_CHECK(action->runnable, action->runnable = TRUE); } /* From Bug #1601, successful fencing must be an input to a failed resources stop action. However given group(rA, rB) running on nodeX and B.stop has failed, A := stop healthy resource (rA.stop) B := stop failed resource (pseudo operation B.stop) C := stonith nodeX A requires B, B requires C, C requires A This loop would prevent the cluster from making progress. This block creates the "C requires A" dependency and therefore must (at least for now) be disabled. Instead, run the block above and treat all resources on nodeX as B would be (marked as a pseudo op depending on the STONITH). TODO: Break the "A requires B" dependency in update_action() and re-enable this block } else if(is_stonith == FALSE) { crm_info("Moving healthy resource %s" " off %s before fencing", rsc->id, node->details->uname); * stop healthy resources before the * stonith op * custom_action_order( rsc, stop_key(rsc), NULL, NULL,crm_strdup(CRM_OP_FENCE),stonith_op, pe_order_optional, data_set); */ ); g_list_free(action_list); key = demote_key(rsc); action_list = find_actions(rsc->actions, key, stonith_op->node); crm_free(key); slist_iter( action, action_t, action_list, lpc2, if(action->node->details->online == FALSE || is_set(rsc->flags, pe_rsc_failed)) { crm_info("Demote of failed resource %s is" " implict after %s is fenced", rsc->id, action->node->details->uname); /* the stop would never complete and is * now implied by the stonith operation */ action->pseudo = TRUE; action->runnable = TRUE; if(is_stonith == FALSE) { order_actions(stonith_op, action, pe_order_optional); } } ); g_list_free(action_list); } void complex_stonith_ordering( resource_t *rsc, action_t *stonith_op, pe_working_set_t *data_set) { gboolean is_stonith = FALSE; const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); if(rsc->children) { slist_iter( child_rsc, resource_t, rsc->children, lpc, child_rsc->cmds->stonith_ordering( child_rsc, stonith_op, data_set); ); return; } if(is_not_set(rsc->flags, pe_rsc_managed)) { crm_debug_3("Skipping fencing constraints for unmanaged resource: %s", rsc->id); return; } if(stonith_op != NULL && safe_str_eq(class, "stonith")) { is_stonith = TRUE; } /* Start constraints */ native_start_constraints(rsc, stonith_op, is_stonith, data_set); /* Stop constraints */ native_stop_constraints(rsc, stonith_op, is_stonith, data_set); } enum stack_activity { stack_stable = 0, stack_starting = 1, stack_stopping = 2, stack_middle = 4, }; static enum stack_activity find_clone_activity_on(resource_t *rsc, resource_t *target, node_t *node, const char *type) { int mode = stack_stable; action_t *active = NULL; if(target->children) { slist_iter( child, resource_t, target->children, lpc, mode |= find_clone_activity_on(rsc, child, node, type); ); return mode; } active = find_first_action(target->actions, NULL, CRMD_ACTION_START, NULL); if(active && active->optional == FALSE && active->pseudo == FALSE) { crm_debug("%s: found scheduled %s action (%s)", rsc->id, active->uuid, type); mode |= stack_starting; } active = find_first_action(target->actions, NULL, CRMD_ACTION_STOP, node); if(active && active->optional == FALSE && active->pseudo == FALSE) { crm_debug("%s: found scheduled %s action (%s)", rsc->id, active->uuid, type); mode |= stack_stopping; } return mode; } static enum stack_activity check_stack_element(resource_t *rsc, resource_t *other_rsc, const char *type) { resource_t *other_p = uber_parent(other_rsc); if(other_rsc == NULL || other_rsc == rsc) { return stack_stable; } else if(other_p->variant == pe_native) { crm_notice("Cannot migrate %s due to dependency on %s (%s)", rsc->id, other_rsc->id, type); return stack_middle; } else if(other_rsc == rsc->parent) { int mode = 0; slist_iter(constraint, rsc_colocation_t, other_rsc->rsc_cons, lpc, if(constraint->score > 0) { mode |= check_stack_element(rsc, constraint->rsc_rh, type); } ); return mode; } else if(other_p->variant == pe_group) { crm_notice("Cannot migrate %s due to dependency on group %s (%s)", rsc->id, other_rsc->id, type); return stack_middle; } /* else: >= clone */ /* ## Assumption A depends on clone(B) ## Resource Activity During Move N1 N2 N3 --- --- --- t0 A.stop t1 B.stop B.stop t2 B.start B.start t3 A.start ## Resource Activity During Migration N1 N2 N3 --- --- --- t0 B.start B.start t1 A.stop (1) t2 A.start (2) t3 B.stop B.stop Node 1: Rewritten to be a migrate-to operation Node 2: Rewritten to be a migrate-from operation # Constraints The following constraints already exist in the system. The 'ok' and 'fail' column refers to whether they still hold for migration. a) A.stop -> A.start - ok b) B.stop -> B.start - fail c) A.stop -> B.stop - ok d) B.start -> A.start - ok e) B.stop -> A.start - fail f) A.stop -> B.start - fail ## Scenarios B unchanged - ok B stopping only - fail - possible after fixing 'e' B starting only - fail - possible after fixing 'f' B stoping and starting - fail - constraint 'b' is unfixable B restarting only on N2 - fail - as-per previous only rarer */ /* Only allow migration when the clone is either stable, only starting or only stopping */ return find_clone_activity_on(rsc, other_rsc, NULL, type); } static gboolean at_stack_bottom(resource_t *rsc) { char *key = NULL; action_t *start = NULL; action_t *other = NULL; int mode = stack_stable; GListPtr action_list = NULL; key = start_key(rsc); action_list = find_actions(rsc->actions, key, NULL); crm_free(key); crm_debug_3("%s: processing", rsc->id); CRM_CHECK(action_list != NULL, return FALSE); start = action_list->data; g_list_free(action_list); slist_iter( constraint, rsc_colocation_t, rsc->rsc_cons, lpc, resource_t *target = constraint->rsc_rh; crm_debug_4("Checking %s: %s == %s (%d)", constraint->id, rsc->id, target->id, constraint->score); if(constraint->score > 0) { mode |= check_stack_element(rsc, target, "coloc"); if(mode & stack_middle) { return FALSE; } else if((mode & stack_stopping) && (mode & stack_starting)) { crm_notice("Cannot migrate %s due to colocation activity (last was %s)", rsc->id, target->id); return FALSE; } } ); slist_iter( other_w, action_wrapper_t, start->actions_before, lpc, other = other_w->action; if(other_w->type & pe_order_serialize_only) { crm_debug_3("%s: depends on %s (serialize ordering)", rsc->id, other->uuid); continue; } crm_debug_2("%s: Checking %s ordering", rsc->id, other->uuid); if(other->optional == FALSE) { mode |= check_stack_element(rsc, other->rsc, "order"); if(mode & stack_middle) { return FALSE; } else if((mode & stack_stopping) && (mode & stack_starting)) { crm_notice("Cannot migrate %s due to ordering activity (last was %s)", rsc->id, other->rsc->id); return FALSE; } } ); return TRUE; } void complex_migrate_reload(resource_t *rsc, pe_working_set_t *data_set) { char *key = NULL; int level = LOG_DEBUG; GListPtr action_list = NULL; action_t *stop = NULL; action_t *start = NULL; action_t *other = NULL; action_t *action = NULL; const char *value = NULL; if(rsc->children) { slist_iter( child_rsc, resource_t, rsc->children, lpc, child_rsc->cmds->migrate_reload(child_rsc, data_set); ); other = NULL; return; } else if(rsc->variant > pe_native) { return; } do_crm_log_unlikely(level+1, "Processing %s", rsc->id); if(is_not_set(rsc->flags, pe_rsc_managed) || is_set(rsc->flags, pe_rsc_failed) || is_set(rsc->flags, pe_rsc_start_pending) || rsc->next_role < RSC_ROLE_STARTED || g_list_length(rsc->running_on) != 1) { do_crm_log_unlikely( level+1, "%s: general resource state: flags=0x%.16llx", rsc->id, rsc->flags); return; } value = g_hash_table_lookup(rsc->meta, XML_OP_ATTR_ALLOW_MIGRATE); if(crm_is_true(value)) { set_bit(rsc->flags, pe_rsc_can_migrate); } if(rsc->next_role > RSC_ROLE_SLAVE) { clear_bit(rsc->flags, pe_rsc_can_migrate); do_crm_log_unlikely( level+1, "%s: resource role: role=%s", rsc->id, role2text(rsc->next_role)); } key = start_key(rsc); action_list = find_actions(rsc->actions, key, NULL); crm_free(key); if(action_list == NULL) { do_crm_log_unlikely(level, "%s: no start action", rsc->id); return; } start = action_list->data; g_list_free(action_list); if(is_not_set(rsc->flags, pe_rsc_can_migrate) && start->allow_reload_conversion == FALSE) { do_crm_log_unlikely(level+1, "%s: no need to continue", rsc->id); return; } key = stop_key(rsc); action_list = find_actions(rsc->actions, key, NULL); crm_free(key); if(action_list == NULL) { do_crm_log_unlikely(level, "%s: no stop action", rsc->id); return; } stop = action_list->data; g_list_free(action_list); action = start; if(action->pseudo || action->optional || action->node == NULL || action->runnable == FALSE) { do_crm_log_unlikely(level, "%s: %s", rsc->id, action->task); return; } action = stop; if(action->pseudo || action->optional || action->node == NULL || action->runnable == FALSE) { do_crm_log_unlikely(level, "%s: %s", rsc->id, action->task); return; } if(is_set(rsc->flags, pe_rsc_can_migrate)) { if(start->node == NULL || stop->node == NULL || stop->node->details == start->node->details) { clear_bit(rsc->flags, pe_rsc_can_migrate); } else if(at_stack_bottom(rsc) == FALSE) { clear_bit(rsc->flags, pe_rsc_can_migrate); } } if(is_set(rsc->flags, pe_rsc_can_migrate)) { action_t *to = NULL; action_t *from = NULL; crm_info("Migrating %s from %s to %s", rsc->id, stop->node->details->uname, start->node->details->uname); crm_free(stop->uuid); crm_free(stop->task); stop->task = crm_strdup(RSC_MIGRATE); stop->uuid = generate_op_key(rsc->id, stop->task, 0); add_hash_param(stop->meta, "migrate_source", stop->node->details->uname); add_hash_param(stop->meta, "migrate_target", start->node->details->uname); /* Create the correct ordering ajustments based on find_clone_activity_on(); */ slist_iter( constraint, rsc_colocation_t, rsc->rsc_cons, lpc, resource_t *target = constraint->rsc_rh; crm_info("Repairing %s: %s == %s (%d)", constraint->id, rsc->id, target->id, constraint->score); if(constraint->score > 0) { int mode = check_stack_element(rsc, target, "coloc"); action_t *clone_stop = find_first_action(target->actions, NULL, RSC_STOP, NULL); action_t *clone_start = find_first_action(target->actions, NULL, RSC_STARTED, NULL); CRM_ASSERT(clone_stop != NULL); CRM_ASSERT(clone_start != NULL); CRM_ASSERT((mode & stack_middle) == 0); CRM_ASSERT(((mode & stack_stopping) && (mode & stack_starting)) == 0); if(mode & stack_stopping) { action_t *clone_stop = find_first_action(target->actions, NULL, RSC_STOP, NULL); action_t *clone_start = find_first_action(target->actions, NULL, RSC_STARTED, NULL); crm_debug("Creating %s.start -> %s.stop ordering", rsc->id, target->id); order_actions(start, clone_stop, pe_order_optional); slist_iter( other_w, action_wrapper_t, start->actions_before, lpc2, /* Needed if the clone's started pseudo-action ever gets printed in the graph */ if(other_w->action == clone_start) { crm_debug("Breaking %s -> %s ordering", other_w->action->uuid, start->uuid); other_w->type = pe_order_none; } ); } else if(mode & stack_starting) { crm_debug("Creating %s.started -> %s.stop ordering", target->id, rsc->id); order_actions(clone_start, stop, pe_order_optional); slist_iter( other_w, action_wrapper_t, clone_stop->actions_before, lpc2, /* Needed if the clone's stop pseudo-action ever gets printed in the graph */ if(other_w->action == stop) { crm_debug("Breaking %s -> %s ordering", other_w->action->uuid, clone_stop->uuid); other_w->type = pe_order_none; } ); } } ); crm_free(start->uuid); crm_free(start->task); start->task = crm_strdup(RSC_MIGRATED); start->uuid = generate_op_key(rsc->id, start->task, 0); add_hash_param(start->meta, "migrate_source_uuid", stop->node->details->id); add_hash_param(start->meta, "migrate_source", stop->node->details->uname); add_hash_param(start->meta, "migrate_target", start->node->details->uname); /* Anything that needed stop to complete, now also needs start to have completed */ slist_iter( other_w, action_wrapper_t, stop->actions_after, lpc, other = other_w->action; if(other->optional || other->rsc != NULL) { continue; } crm_debug("Ordering %s before %s (stop)", start->uuid, other_w->action->uuid); order_actions(start, other, other_w->type); ); /* Stop also needs anything that the start needed to have completed too */ slist_iter( other_w, action_wrapper_t, start->actions_before, lpc, other = other_w->action; if(other->rsc == NULL) { /* nothing */ } else if(other->optional || other->rsc == rsc || other->rsc == rsc->parent) { continue; } crm_debug("Ordering %s before %s (start)", other_w->action->uuid, stop->uuid); order_actions(other, stop, other_w->type); ); /* Overwrite any op-specific params with those from the migrate ops */ from = custom_action( rsc, crm_strdup(start->uuid), start->task, start->node, FALSE, FALSE, data_set); g_hash_table_foreach(start->meta, append_hashtable, from->meta); g_hash_table_destroy(start->meta); start->meta = from->meta; from->meta = NULL; pe_free_action(from); to = custom_action( rsc, crm_strdup(stop->uuid), stop->task, stop->node, FALSE, FALSE, data_set); g_hash_table_foreach(stop->meta, append_hashtable, to->meta); g_hash_table_destroy(stop->meta); stop->meta = to->meta; to->meta = NULL; pe_free_action(to); } else if(start && stop && start->allow_reload_conversion && stop->node->details == start->node->details) { action_t *rewrite = NULL; start->pseudo = TRUE; /* easier than trying to delete it from the graph */ action = NULL; key = promote_key(rsc); action_list = find_actions(rsc->actions, key, NULL); if(action_list) { action = action_list->data; } if(action && action->optional == FALSE) { action->pseudo = TRUE; } g_list_free(action_list); crm_free(key); action = NULL; key = demote_key(rsc); action_list = find_actions(rsc->actions, key, NULL); if(action_list) { action = action_list->data; } g_list_free(action_list); crm_free(key); if(action && action->optional == FALSE) { rewrite = action; stop->pseudo = TRUE; } else { rewrite = stop; } crm_info("Rewriting %s of %s on %s as a reload", rewrite->task, rsc->id, stop->node->details->uname); crm_free(rewrite->uuid); crm_free(rewrite->task); rewrite->task = crm_strdup("reload"); rewrite->uuid = generate_op_key(rsc->id, rewrite->task, 0); } else { do_crm_log_unlikely(level+1, "%s nothing to do", rsc->id); } } void native_append_meta(resource_t *rsc, xmlNode *xml) { char *value = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_INCARNATION); if(value) { char *name = NULL; name = crm_meta_name(XML_RSC_ATTR_INCARNATION); crm_xml_add(xml, name, value); crm_free(name); } } diff --git a/pengine/ptest.supp b/pengine/ptest.supp index be2261f48e..7856ff5ff9 100644 --- a/pengine/ptest.supp +++ b/pengine/ptest.supp @@ -1,18 +1,18 @@ { Libxml2 Mutex Memcheck:Leak fun:malloc fun:xmlNewRMutex fun:xmlDictFree fun:xmlHashFree fun:xmlFreeDoc } { GLib global allocator object Memcheck:Leak fun:calloc fun:g_malloc0 - obj:/usr/lib64/libglib-2.0.so.0.1600.3 + obj:* fun:g_slice_alloc }