diff --git a/include/crm/crm.h b/include/crm/crm.h index bae3806d69..015504f7de 100644 --- a/include/crm/crm.h +++ b/include/crm/crm.h @@ -1,321 +1,347 @@ /* * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net> * * 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 */ #ifndef CRM__H #define CRM__H #include <config.h> #include <stdlib.h> #include <glib.h> #undef MIN #undef MAX #include <string.h> #include <clplumbing/cl_log.h> #include <clplumbing/cl_malloc.h> #ifdef MCHECK #include <mcheck.h> #endif #include <libxml/tree.h> #define EOS '\0' #define DIMOF(a) ((int) (sizeof(a)/sizeof(a[0])) ) #define HAURL(url) HA_URLBASE url #ifndef CRM_DEV_BUILD # define CRM_DEV_BUILD 0 #endif #define CRM_DEPRECATED_SINCE_2_0_1 0 #define CRM_DEPRECATED_SINCE_2_0_2 0 #define CRM_DEPRECATED_SINCE_2_0_3 0 #define CRM_DEPRECATED_SINCE_2_0_4 0 #define CRM_DEPRECATED_SINCE_2_0_5 0 #define CRM_DEPRECATED_SINCE_2_0_6 1 #define CRM_DEPRECATED_SINCE_2_0_7 1 #define CRM_DEPRECATED_SINCE_2_0_8 1 #define CRM_DEPRECATED_SINCE_2_1_0 1 #define CRM_META "CRM_meta" #define crm_meta_name(field) CRM_META"_"field #define ipc_call_diff_max_ms 5000 #define action_diff_warn_ms 5000 #define action_diff_max_ms 20000 #define fsa_diff_warn_ms 10000 #define fsa_diff_max_ms 30000 #include <crm/common/util.h> #define CRM_ASSERT(expr) if((expr) == FALSE) { \ crm_abort(__FILE__, __PRETTY_FUNCTION__, __LINE__, #expr, TRUE, FALSE); \ } extern gboolean crm_assert_failed; #define CRM_DEV_ASSERT(expr) \ crm_assert_failed = FALSE; \ if((expr) == FALSE) { \ crm_assert_failed = TRUE; \ crm_abort(__FILE__,__PRETTY_FUNCTION__,__LINE__, #expr, FALSE, TRUE); \ } #define CRM_CHECK(expr, failure_action) if((expr) == FALSE) { \ crm_abort(__FILE__,__PRETTY_FUNCTION__,__LINE__, #expr, FALSE, TRUE); \ failure_action; \ } #define CRM_CHECK_AND_STORE(expr, failure_action) if((expr) == FALSE) { \ crm_abort(__FILE__,__PRETTY_FUNCTION__,__LINE__, #expr, TRUE, TRUE); \ failure_action; \ } extern const char *crm_system_name; /* Clean these up at some point, some probably should be runtime options */ #define WORKING_DIR HA_VARLIBDIR"/heartbeat/crm" #define CRM_SOCK_DIR HA_VARRUNDIR"/heartbeat/crm" #define BIN_DIR HA_LIBDIR"/heartbeat" #define SOCKET_LEN 1024 #define APPNAME_LEN 256 #define MAX_IPC_FAIL 5 #define CIB_FILENAME WORKING_DIR"/cib.xml" #define CIB_BACKUP WORKING_DIR"/cib_backup.xml" #define CRM_FEATURE_SET "2.1" #define MSG_LOG 1 #define DOT_FSA_ACTIONS 1 #define DOT_ALL_FSA_INPUTS 1 /* #define FSA_TRACE 1 */ #define INFINITY_S "INFINITY" #define MINUS_INFINITY_S "-INFINITY" #define INFINITY 1000000 /* Sub-systems */ #define CRM_SYSTEM_DC "dc" #define CRM_SYSTEM_DCIB "dcib" /* The master CIB */ #define CRM_SYSTEM_CIB "cib" #define CRM_SYSTEM_CRMD "crmd" #define CRM_SYSTEM_LRMD "lrmd" #define CRM_SYSTEM_PENGINE "pengine" #define CRM_SYSTEM_TENGINE "tengine" /* Valid operations */ #define CRM_OP_NOOP "noop" #define CRM_OP_JOIN_ANNOUNCE "join_announce" #define CRM_OP_JOIN_OFFER "join_offer" #define CRM_OP_JOIN_REQUEST "join_request" #define CRM_OP_JOIN_ACKNAK "join_ack_nack" #define CRM_OP_JOIN_CONFIRM "join_confirm" #define CRM_OP_DIE "die_no_respawn" #define CRM_OP_RETRIVE_CIB "retrieve_cib" #define CRM_OP_PING "ping" #define CRM_OP_VOTE "vote" #define CRM_OP_NOVOTE "no-vote" #define CRM_OP_HELLO "hello" #define CRM_OP_HBEAT "dc_beat" #define CRM_OP_PECALC "pe_calc" #define CRM_OP_ABORT "abort" #define CRM_OP_QUIT "quit" #define CRM_OP_LOCAL_SHUTDOWN "start_shutdown" #define CRM_OP_SHUTDOWN_REQ "req_shutdown" #define CRM_OP_SHUTDOWN "do_shutdown" #define CRM_OP_FENCE "stonith" #define CRM_OP_EVENTCC "event_cc" #define CRM_OP_TEABORT "te_abort" #define CRM_OP_TEABORTED "te_abort_confirmed" /* we asked */ #define CRM_OP_TE_HALT "te_halt" #define CRM_OP_TECOMPLETE "te_complete" #define CRM_OP_TETIMEOUT "te_timeout" #define CRM_OP_TRANSITION "transition" #define CRM_OP_REGISTER "register" #define CRM_OP_DEBUG_UP "debug_inc" #define CRM_OP_DEBUG_DOWN "debug_dec" #define CRM_OP_INVOKE_LRM "lrm_invoke" #define CRM_OP_LRM_REFRESH "lrm_refresh" #define CRM_OP_LRM_QUERY "lrm_query" #define CRM_OP_LRM_DELETE "lrm_delete" #define CRM_OP_LRM_FAIL "lrm_fail" #define CRM_OP_PROBED "probe_complete" #define CRM_OP_REPROBE "probe_again" #define CRMD_STATE_ACTIVE "member" #define CRMD_STATE_INACTIVE "down" #define CRMD_JOINSTATE_DOWN CRMD_STATE_INACTIVE #define CRMD_JOINSTATE_PENDING "pending" #define CRMD_JOINSTATE_MEMBER CRMD_STATE_ACTIVE #define CRMD_ACTION_DELETE "delete" #define CRMD_ACTION_CANCEL "cancel" #define CRMD_ACTION_MIGRATE "migrate_to" #define CRMD_ACTION_MIGRATED "migrate_from" #define CRMD_ACTION_START "start" #define CRMD_ACTION_STARTED "running" #define CRMD_ACTION_STOP "stop" #define CRMD_ACTION_STOPPED "stopped" #define CRMD_ACTION_PROMOTE "promote" #define CRMD_ACTION_PROMOTED "promoted" #define CRMD_ACTION_DEMOTE "demote" #define CRMD_ACTION_DEMOTED "demoted" #define CRMD_ACTION_NOTIFY "notify" #define CRMD_ACTION_NOTIFIED "notified" #define CRMD_ACTION_STATUS "monitor" +/* short names */ +#define RSC_DELETE CRMD_ACTION_DELETE +#define RSC_CANCEL CRMD_ACTION_CANCEL + +#define RSC_MIGRATE CRMD_ACTION_MIGRATE +#define RSC_MIGRATED CRMD_ACTION_MIGRATED + +#define RSC_START CRMD_ACTION_START +#define RSC_STARTED CRMD_ACTION_STARTED + +#define RSC_STOP CRMD_ACTION_STOP +#define RSC_STOPPED CRMD_ACTION_STOPPED + +#define RSC_PROMOTE CRMD_ACTION_PROMOTE +#define RSC_PROMOTED CRMD_ACTION_PROMOTED +#define RSC_DEMOTE CRMD_ACTION_DEMOTE +#define RSC_DEMOTED CRMD_ACTION_DEMOTED + +#define RSC_NOTIFY CRMD_ACTION_NOTIFY +#define RSC_NOTIFIED CRMD_ACTION_NOTIFIED + +#define RSC_STATUS CRMD_ACTION_STATUS + + + + typedef GList* GListPtr; #define slist_destroy(child_type, child, parent, a) \ { \ GListPtr __crm_iter_head = parent; \ child_type *child = NULL; \ while(__crm_iter_head != NULL) { \ child = (child_type *) __crm_iter_head->data; \ __crm_iter_head = __crm_iter_head->next; \ { a; } \ } \ g_list_free(parent); \ } #define slist_iter(child, child_type, parent, counter, a) \ { \ GListPtr __crm_iter_head = parent; \ child_type *child = NULL; \ int counter = 0; \ for(; __crm_iter_head != NULL; counter++) { \ child = (child_type *) __crm_iter_head->data; \ __crm_iter_head = __crm_iter_head->next; \ { a; } \ } \ } #define LOG_DEBUG_2 LOG_DEBUG+1 #define LOG_DEBUG_3 LOG_DEBUG+2 #define LOG_DEBUG_4 LOG_DEBUG+3 #define LOG_DEBUG_5 LOG_DEBUG+4 #define LOG_DEBUG_6 LOG_DEBUG+5 #define LOG_MSG LOG_DEBUG_3 /* * Throughout the macros below, note the leading, pre-comma, space in the * various ' , ##args' occurences to aid portability across versions of 'gcc'. * http://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html#Variadic-Macros */ #define do_crm_log(level, fmt, args...) do { \ if(crm_log_level < (level)) { \ continue; \ } else if((level) > LOG_DEBUG) { \ cl_log(LOG_DEBUG, "debug%d: %s: " fmt, \ level-LOG_INFO, __PRETTY_FUNCTION__ , ##args); \ } else { \ cl_log(level, "%s: " fmt, \ __PRETTY_FUNCTION__ , ##args); \ } \ } while(0) #define crm_crit(fmt, args...) do_crm_log(LOG_CRIT, fmt , ##args) #define crm_err(fmt, args...) do_crm_log(LOG_ERR, fmt , ##args) #define crm_warn(fmt, args...) do_crm_log(LOG_WARNING, fmt , ##args) #define crm_notice(fmt, args...) do_crm_log(LOG_NOTICE, fmt , ##args) #define crm_info(fmt, args...) do_crm_log(LOG_INFO, fmt , ##args) #define crm_debug(fmt, args...) do_crm_log(LOG_DEBUG, fmt , ##args) #define crm_debug_2(fmt, args...) do_crm_log(LOG_DEBUG_2, fmt , ##args) #define crm_debug_3(fmt, args...) do_crm_log(LOG_DEBUG_3, fmt , ##args) #define crm_debug_4(fmt, args...) do_crm_log(LOG_DEBUG_4, fmt , ##args) #define crm_debug_5(fmt, args...) do_crm_log(LOG_DEBUG_5, fmt , ##args) #define crm_debug_6(fmt, args...) do_crm_log(LOG_DEBUG_6, fmt , ##args) extern void crm_log_message_adv( int level, const char *alt_debugfile, const HA_Message *msg); #define crm_log_xml(level, text, xml) if(crm_log_level >= (level)) { \ print_xml_formatted(level, __PRETTY_FUNCTION__, xml, text); \ } #define crm_log_xml_crit(xml, text) crm_log_xml(LOG_CRIT, text, xml) #define crm_log_xml_err(xml, text) crm_log_xml(LOG_ERR, text, xml) #define crm_log_xml_warn(xml, text) crm_log_xml(LOG_WARNING, text, xml) #define crm_log_xml_notice(xml, text) crm_log_xml(LOG_NOTICE, text, xml) #define crm_log_xml_info(xml, text) crm_log_xml(LOG_INFO, text, xml) #define crm_log_xml_debug(xml, text) crm_log_xml(LOG_DEBUG, text, xml) #define crm_log_xml_debug_2(xml, text) crm_log_xml(LOG_DEBUG_2, text, xml) #define crm_log_xml_debug_3(xml, text) crm_log_xml(LOG_DEBUG_3, text, xml) #define crm_log_xml_debug_4(xml, text) crm_log_xml(LOG_DEBUG_4, text, xml) #define crm_log_xml_debug_5(xml, text) crm_log_xml(LOG_DEBUG_5, text, xml) #define crm_str(x) (const char*)(x?x:"<null>") #if CRM_DEV_BUILD # define crm_malloc0(malloc_obj, length) do { \ if(malloc_obj) { \ crm_err("Potential memory leak:" \ " %s at %s:%d not NULL before alloc.", \ #malloc_obj, __FILE__, __LINE__); \ } \ malloc_obj = cl_malloc(length); \ if(malloc_obj == NULL) { \ crm_err("Failed allocation of %lu bytes", (unsigned long)length); \ CRM_ASSERT(malloc_obj != NULL); \ } \ memset(malloc_obj, 0, length); \ } while(0) /* it's not a memory leak to already have an object to realloc, that's * the usual case, however if it does have a value, it must have been * allocated by the same allocator! */ # define crm_realloc(realloc_obj, length) do { \ if (realloc_obj != NULL) { \ CRM_ASSERT(cl_is_allocated(realloc_obj) == 1); \ } \ realloc_obj = cl_realloc(realloc_obj, length); \ CRM_ASSERT(realloc_obj != NULL); \ } while(0) # define crm_free(free_obj) if(free_obj) { \ CRM_ASSERT(cl_is_allocated(free_obj) == 1); \ cl_free(free_obj); \ free_obj=NULL; \ } #else # define crm_malloc0(malloc_obj, length) do { \ malloc_obj = cl_malloc(length); \ if(malloc_obj == NULL) { \ crm_err("Failed allocation of %lu bytes", (unsigned long)length); \ CRM_ASSERT(malloc_obj != NULL); \ } \ memset(malloc_obj, 0, length); \ } while(0) # define crm_realloc(realloc_obj, length) do { \ realloc_obj = cl_realloc(realloc_obj, length); \ CRM_ASSERT(realloc_obj != NULL); \ } while(0) # define crm_free(free_obj) if(free_obj) { cl_free(free_obj); free_obj=NULL; } #endif #define crm_msg_del(msg) if(msg != NULL) { ha_msg_del(msg); msg = NULL; } #define crm_strdup(str) crm_strdup_fn(str, __FILE__, __PRETTY_FUNCTION__, __LINE__) #endif diff --git a/pengine/allocate.c b/pengine/allocate.c index 6ead624a46..228130b4c1 100644 --- a/pengine/allocate.c +++ b/pengine/allocate.c @@ -1,950 +1,948 @@ /* * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net> * * 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 <crm_internal.h> #include <sys/param.h> #include <crm/crm.h> #include <crm/cib.h> #include <crm/msg_xml.h> #include <crm/common/xml.h> #include <crm/common/msg.h> #include <clplumbing/cl_misc.h> #include <glib.h> #include <crm/pengine/status.h> #include <pengine.h> #include <allocate.h> #include <utils.h> #include <lib/crm/pengine/utils.h> 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, complex_create_notify_element, }, { 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, complex_create_notify_element, }, { 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, complex_create_notify_element, }, { 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, complex_create_notify_element, } }; 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 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 && data_set->stop_action_orphans) { /* create a cancel action */ action_t *cancel = NULL; char *cancel_key = crm_strdup(key); const char *call_id = crm_element_value(xml_op, XML_LRM_ATTR_CALLID); crm_info("Orphan action will be stopped: %s on %s", key, active_node->details->uname); /* cancel_key = generate_op_key( */ - /* rsc->id, CRMD_ACTION_CANCEL, interval); */ + /* rsc->id, RSC_CANCEL, interval); */ cancel = custom_action( - rsc, cancel_key, CRMD_ACTION_CANCEL, + rsc, cancel_key, RSC_CANCEL, active_node, FALSE, TRUE, data_set); crm_free(cancel->task); - cancel->task = crm_strdup(CRMD_ACTION_CANCEL); + 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; 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); unpack_instance_attributes( rsc->xml, XML_TAG_ATTR_SETS, active_node->details->attrs, local_rsc_params, NULL, FALSE, data_set->now); params_all = create_xml_node(NULL, XML_TAG_PARAMS); 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); g_hash_table_foreach(local_rsc_params, hash2field, 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, CRMD_ACTION_START, TRUE)) { + 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)); 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, node_t *node, pe_working_set_t *data_set) { const char *id = NULL; const char *task = NULL; int interval = 0; const char *interval_s = NULL; GListPtr op_list = NULL; GListPtr sorted_op_list = NULL; const char *rsc_id = ID(rsc_entry); gboolean is_probe = FALSE; int start_index = 0, stop_index = 0; resource_t *rsc = pe_find_resource(data_set->resources, rsc_id); CRM_CHECK(rsc != NULL, return); CRM_CHECK(node != NULL, return); CRM_CHECK(rsc_id != 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, CRMD_ACTION_STATUS)) { + if(interval == 0 && safe_str_eq(task, RSC_STATUS)) { is_probe = TRUE; } - if(is_probe || safe_str_eq(task, CRMD_ACTION_START) || interval > 0) { + 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 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 || data_set->stonith_enabled) { xml_child_iter_filter( lrm_rscs, rsc_entry, XML_LRM_TAG_RESOURCE, if(xml_has_children(rsc_entry)) { check_actions_for(rsc_entry, node, data_set); } ); } ); } 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; const char *value = NULL; resource_t *failed = rsc; GHashTable *meta_hash = NULL; if(rsc->children) { slist_iter( child_rsc, resource_t, rsc->children, lpc, common_apply_stickiness(child_rsc, node, data_set); ); return; } meta_hash = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); get_meta_attributes(meta_hash, rsc, node, data_set); /* update resource preferences that relate to the current node */ value = g_hash_table_lookup(meta_hash, "resource_stickiness"); if(value != NULL && safe_str_neq("default", value)) { rsc->stickiness = char2score(value); } else { rsc->stickiness = data_set->default_resource_stickiness; } value = g_hash_table_lookup(meta_hash, XML_RSC_ATTR_FAIL_STICKINESS); if(value != NULL && safe_str_neq("default", value)) { rsc->migration_threshold = char2score(value); } else { rsc->migration_threshold = data_set->default_migration_threshold; } 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); } } g_hash_table_destroy(meta_hash); } 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); ); } 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); 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); ); ); 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); crm_debug_2("%s probed: %s", node->details->uname, 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); - custom_action_order(NULL, NULL, probe_node_complete, - NULL, NULL, probe_complete, - pe_order_optional, data_set); + order_actions(probe_node_complete, probe_complete, pe_order_optional); 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); } ); ); 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); 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) { /* 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); ); 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); gboolean need_stonith = FALSE; crm_debug_3("Processing fencing and shutdown cases"); if(data_set->stonith_enabled && (data_set->have_quorum || data_set->no_quorum_policy == no_quorum_ignore)) { need_stonith = TRUE; } if(need_stonith && any_managed_resouces(data_set) == FALSE) { crm_crit("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); 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(data_set->have_quorum == FALSE) { crm_notice("Cannot fence unclean nodes until quorum is" " attained (or no_quorum_policy is set to ignore)"); } else if(data_set->stonith_enabled == FALSE) { pe_warn("YOUR RESOURCES ARE NOW LIKELY COMPROMISED"); pe_err("ENABLE STONITH TO KEEP YOUR RESOURCES SAFE"); } } 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); } 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_2("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); } ); update_action_states(data_set->actions); slist_iter( rsc, resource_t, data_set->resources, lpc, rsc->cmds->migrate_reload(rsc, data_set); ); return TRUE; } 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; char *transition_id_s = NULL; transition_id++; transition_id_s = crm_itoa(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); crm_xml_add(data_set->graph, "failed-stop-offset", "INFINITY"); value = pe_pref(data_set->config_hash, "start-failure-is-fatal"); if(crm_is_true(value)) { 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(data_set->graph, "transition_id", transition_id_s); crm_free(transition_id_s); /* 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 342b485431..0264d5eec3 100644 --- a/pengine/clone.c +++ b/pengine/clone.c @@ -1,1484 +1,1455 @@ /* * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net> * * 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 <crm_internal.h> #include <crm/msg_xml.h> #include <allocate.h> #include <utils.h> #include <lib/crm/pengine/utils.h> #define VARIANT_CLONE 1 #include <lib/crm/pengine/variant.h> gint sort_clone_instance(gconstpointer a, gconstpointer b); void clone_create_notifications( resource_t *rsc, action_t *action, action_t *action_complete, pe_working_set_t *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) { int level = LOG_DEBUG_3; node_t *node1 = NULL; node_t *node2 = NULL; gboolean can1 = TRUE; gboolean can2 = TRUE; const resource_t *resource1 = (const resource_t*)a; const resource_t *resource2 = (const resource_t*)b; CRM_ASSERT(resource1 != NULL); CRM_ASSERT(resource2 != NULL); /* allocation order: * - active instances * - instances running on nodes with the least copies * - active instances on nodes that cant support them or are to be fenced * - failed instances * - inactive instances */ do_crm_log(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(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(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->weight < 0) { do_crm_log(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->weight < 0) { do_crm_log(level, "%s: current location is unavailable", resource2->id); node2 = NULL; can2 = FALSE; } } if(can1 != can2) { if(can1) { do_crm_log(level, "%s < %s: availability of current location", resource1->id, resource2->id); return -1; } do_crm_log(level, "%s > %s: availability of current location", resource1->id, resource2->id); return 1; } if(resource1->priority < resource2->priority) { do_crm_log(level, "%s < %s: priority", resource1->id, resource2->id); return 1; } else if(resource1->priority > resource2->priority) { do_crm_log(level, "%s > %s: priority", resource1->id, resource2->id); return -1; } if(node1 == NULL && node2 == NULL) { do_crm_log(level, "%s == %s: not active", resource1->id, resource2->id); return 0; } if(node1 != node2) { if(node1 == NULL) { do_crm_log(level, "%s > %s: active", resource1->id, resource2->id); return 1; } else if(node2 == NULL) { do_crm_log(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(level, "%s < %s: can", resource1->id, resource2->id); return -1; } do_crm_log(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(level, "%s < %s: not allowed", resource1->id, resource2->id); return -1; } else if(node1 == NULL && node2 != NULL) { do_crm_log(level, "%s > %s: not allowed", resource1->id, resource2->id); return 1; } if(node1 == NULL) { do_crm_log(level, "%s == %s: not allowed", resource1->id, resource2->id); return 0; } if(node1->count < node2->count) { do_crm_log(level, "%s < %s: count", resource1->id, resource2->id); return -1; } else if(node1->count > node2->count) { do_crm_log(level, "%s > %s: count", resource1->id, resource2->id); return 1; } if(node1->weight < node2->weight) { do_crm_log(level, "%s < %s: node score", resource1->id, resource2->id); return 1; } else if(node1->weight > node2->weight) { do_crm_log(level, "%s > %s: node score", resource1->id, resource2->id); return -1; } can1 = did_fail(resource1); can2 = did_fail(resource2); if(can1 != can2) { if(can1) { do_crm_log(level, "%s > %s: failed", resource1->id, resource2->id); return 1; } do_crm_log(level, "%s < %s: failed", resource1->id, resource2->id); return -1; } do_crm_log(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; } 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("Dependancy 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) { slist_iter(cons, rsc_colocation_t, rsc->rsc_cons, lpc, child->rsc_cons = g_list_append(child->rsc_cons, cons)); slist_iter(cons, rsc_colocation_t, rsc->rsc_cons_lhs, lpc, 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; 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("Dependancy 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->score/INFINITY, TRUE); ); dump_node_scores(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(rsc->children, sort_clone_instance); /* 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( rsc->allowed_nodes, sort_node_weight); 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 { append_parent_colocation(rsc, child); } 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) { if(rsc->children) { slist_iter(child, resource_t, rsc->children, lpc, clone_update_pseudo_status(child, stopping, starting) ); return; } CRM_ASSERT(stopping != NULL); CRM_ASSERT(starting != NULL); 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(CRMD_ACTION_STOP, action->task)) { + } 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(CRMD_ACTION_START, action->task)) { + } 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; } } ); } void clone_create_actions(resource_t *rsc, pe_working_set_t *data_set) { gboolean child_starting = FALSE; gboolean child_stopping = FALSE; action_t *stop = NULL; action_t *start = NULL; action_t *action_complete = 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); 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); action_complete = custom_action( rsc, started_key(rsc), - CRMD_ACTION_STARTED, NULL, !child_starting, TRUE, data_set); + RSC_STARTED, NULL, !child_starting, TRUE, data_set); start->pseudo = TRUE; start->runnable = TRUE; action_complete->pseudo = TRUE; action_complete->runnable = TRUE; action_complete->priority = INFINITY; /* crm_err("Upgrading priority for %s to INFINITY", action_complete->uuid); */ child_starting_constraints(clone_data, rsc, NULL, last_start_rsc, data_set); clone_create_notifications( rsc, start, action_complete, data_set); /* stop */ stop = stop_action(rsc, NULL, !child_stopping); action_complete = custom_action( rsc, stopped_key(rsc), - CRMD_ACTION_STOPPED, NULL, !child_stopping, TRUE, data_set); + RSC_STOPPED, NULL, !child_stopping, TRUE, data_set); stop->pseudo = TRUE; stop->runnable = TRUE; action_complete->pseudo = TRUE; action_complete->runnable = TRUE; action_complete->priority = INFINITY; /* crm_err("Upgrading priority for %s to INFINITY", action_complete->uuid); */ child_stopping_constraints(clone_data, rsc, NULL, last_stop_rsc, data_set); clone_create_notifications(rsc, stop, action_complete, data_set); rsc->actions = rsc->actions; if(stop->post_notified != NULL && start->pre_notify != NULL) { order_actions(stop->post_notified, start->pre_notify, pe_order_optional); } } void clone_create_notifications( resource_t *rsc, action_t *action, action_t *action_complete, pe_working_set_t *data_set) { /* * pre_notify -> pre_notify_complete -> pseudo_action * -> (real actions) -> pseudo_action_complete * -> post_notify -> post_notify_complete * * if the pre_noitfy requires confirmation, * then a list of confirmations will be added as triggers * to pseudo_action in clone_expand() */ action_t *notify = NULL; action_t *notify_complete = NULL; enum action_tasks task; char *notify_key = NULL; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); if(is_not_set(rsc->flags, pe_rsc_notify)) { return; } task = text2task(action->task); /* create pre_notify */ notify_key = generate_notify_key( rsc->id, "pre", action->task); notify = custom_action(rsc, notify_key, - CRMD_ACTION_NOTIFY, NULL, + RSC_NOTIFY, NULL, action->optional, TRUE, data_set); add_hash_param(notify->meta, "notify_type", "pre"); add_hash_param(notify->meta, "notify_operation", action->task); if(clone_data->notify_confirm) { add_hash_param(notify->meta, "notify_confirm", "yes"); } else { add_hash_param(notify->meta, "notify_confirm", "no"); } /* create pre_notify_complete */ notify_key = generate_notify_key( rsc->id, "confirmed-pre", action->task); notify_complete = custom_action(rsc, notify_key, - CRMD_ACTION_NOTIFIED, NULL, + RSC_NOTIFIED, NULL, action->optional, TRUE, data_set); add_hash_param(notify_complete->meta, "notify_type", "pre"); add_hash_param(notify_complete->meta, "notify_operation", action->task); if(clone_data->notify_confirm) { add_hash_param(notify->meta, "notify_confirm", "yes"); } else { add_hash_param(notify->meta, "notify_confirm", "no"); } notify->pseudo = TRUE; notify->runnable = TRUE; notify_complete->pseudo = TRUE; notify_complete->runnable = TRUE; /* pre_notify before pre_notify_complete */ - custom_action_order( - rsc, NULL, notify, - rsc, NULL, notify_complete, - pe_order_optional, data_set); + order_actions(notify, notify_complete, pe_order_optional); /* pre_notify_complete before action */ - custom_action_order( - rsc, NULL, notify_complete, - rsc, NULL, action, - pe_order_optional, data_set); + order_actions(notify_complete, action, pe_order_optional); action->pre_notify = notify; action->pre_notified = notify_complete; /* create post_notify */ notify_key = generate_notify_key (rsc->id, "post", action->task); notify = custom_action(rsc, notify_key, - CRMD_ACTION_NOTIFY, NULL, + RSC_NOTIFY, NULL, action_complete->optional, TRUE, data_set); add_hash_param(notify->meta, "notify_type", "post"); add_hash_param(notify->meta, "notify_operation", action->task); if(clone_data->notify_confirm) { add_hash_param(notify->meta, "notify_confirm", "yes"); } else { add_hash_param(notify->meta, "notify_confirm", "no"); } /* action_complete before post_notify */ - custom_action_order( - rsc, NULL, action_complete, - rsc, NULL, notify, - pe_order_optional, data_set); + order_actions(action_complete, notify, pe_order_optional); /* create post_notify_complete */ notify_key = generate_notify_key( rsc->id, "confirmed-post", action->task); notify_complete = custom_action(rsc, notify_key, - CRMD_ACTION_NOTIFIED, NULL, + RSC_NOTIFIED, NULL, action->optional, TRUE, data_set); add_hash_param(notify_complete->meta, "notify_type", "pre"); add_hash_param(notify_complete->meta, "notify_operation", action->task); if(clone_data->notify_confirm) { add_hash_param(notify->meta, "notify_confirm", "yes"); } else { add_hash_param(notify->meta, "notify_confirm", "no"); } notify->pseudo = TRUE; notify->runnable = TRUE; notify->priority = INFINITY; notify->runnable = action_complete->runnable; notify_complete->pseudo = TRUE; notify_complete->runnable = TRUE; notify_complete->priority = INFINITY; notify_complete->runnable = action_complete->runnable; /* post_notify before post_notify_complete */ - custom_action_order( - rsc, NULL, notify, - rsc, NULL, notify_complete, - pe_order_optional, data_set); + order_actions(notify, notify_complete, pe_order_optional); action->post_notify = notify; action->post_notified = notify_complete; - if(safe_str_eq(action->task, CRMD_ACTION_STOP)) { + if(safe_str_eq(action->task, RSC_STOP)) { /* post_notify_complete before start */ custom_action_order( rsc, NULL, notify_complete, rsc, start_key(rsc), NULL, pe_order_optional, data_set); - } else if(safe_str_eq(action->task, CRMD_ACTION_START)) { + } else if(safe_str_eq(action->task, RSC_START)) { /* post_notify_complete before promote */ custom_action_order( rsc, NULL, notify_complete, rsc, promote_key(rsc), NULL, pe_order_optional, data_set); - } else if(safe_str_eq(action->task, CRMD_ACTION_DEMOTE)) { + } else if(safe_str_eq(action->task, RSC_DEMOTE)) { /* post_notify_complete before promote */ custom_action_order( rsc, NULL, notify_complete, rsc, stop_key(rsc), NULL, pe_order_optional, data_set); } } 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); - custom_action_order( - child, start_key(child), NULL, - rsc, started_key(rsc), NULL, - pe_order_implies_right_printed, data_set); + new_rsc_order(child, RSC_START, rsc, RSC_STARTED, + pe_order_implies_right_printed, data_set); } if(clone_data->ordered) { if(child == NULL) { - /* last child start before global started */ - custom_action_order( - last, start_key(last), NULL, - rsc, started_key(rsc), NULL, - pe_order_runnable_left, data_set); + /* 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); - custom_action_order( - child, stop_key(child), NULL, - rsc, stopped_key(rsc), NULL, - pe_order_implies_right_printed, data_set); + new_rsc_order(child, RSC_STOP, rsc, RSC_STOPPED, + pe_order_implies_right_printed, data_set); } if(clone_data->ordered) { if(last == NULL) { - /* first child stop before global stopped */ - custom_action_order( - child, stop_key(child), NULL, - rsc, stopped_key(rsc), NULL, - pe_order_runnable_left, data_set); + /* 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 */ - custom_action_order( - rsc, stop_key(rsc), NULL, - rsc, stopped_key(rsc), NULL, - pe_order_runnable_left, data_set); + new_rsc_order(rsc, RSC_STOP, rsc, RSC_STOPPED, pe_order_runnable_left, data_set); /* global start before started */ - custom_action_order( - rsc, start_key(rsc), NULL, - rsc, started_key(rsc), NULL, - pe_order_runnable_left, data_set); + new_rsc_order(rsc, RSC_START, rsc, RSC_STARTED, pe_order_runnable_left, data_set); /* global stopped before start */ - custom_action_order( - rsc, stopped_key(rsc), NULL, - rsc, start_key(rsc), NULL, - pe_order_optional, data_set); + 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_and(lhs, rhs, FALSE); 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: %d", constraint->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_and(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); ); } 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_2("%s->%s", order->lh_action_task, order->rh_action_task); 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_clone || order->rh_rsc->variant == pe_master) { 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) { 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(do_interleave && order->rh_rsc) { 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_debug("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->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 */ crm_debug_2("Clone-to-* ordering: %s -> %s 0x%.6x", order->lh_action_task, order->rh_action_task, order->type); slist_iter( child_rsc, resource_t, rsc->children, lpc, native_rsc_order_lh(child_rsc, order, data_set); ); } } convert_non_atomic_task(rsc, order, FALSE); native_rsc_order_lh(rsc, order, data_set); } if(is_set(rsc->flags, pe_rsc_notify)) { order->type = pe_order_optional; convert_non_atomic_task(rsc, order, TRUE); native_rsc_order_lh(rsc, order, data_set); } } void clone_rsc_order_rh( action_t *lh_action, resource_t *rsc, order_constraint_t *order) { clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); crm_debug_2("%s->%s", lh_action->uuid, 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; } } native_rsc_order_rh(lh_action, rsc, order); } 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); ); } 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, int clones, char **rsc_list, char **node_list, char **uuid_list) { const char *uname = NULL; const char *rsc_id = NULL; const char *last_rsc_id = NULL; CRM_CHECK(list != NULL, return); if(rsc_list) { CRM_CHECK(*rsc_list == NULL, *rsc_list = NULL); } if(node_list) { CRM_CHECK(*node_list == NULL, *node_list = NULL); } slist_iter(entry, notify_entry_t, list, lpc, CRM_CHECK(entry != NULL, continue); CRM_CHECK(entry->rsc != NULL, continue); CRM_CHECK(entry->node != NULL, continue); rsc_id = entry->rsc->id; uname = entry->node->details->uname; CRM_ASSERT(uname != NULL); 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(node_list != NULL) { 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 mark_notifications_required(resource_t *rsc, enum action_tasks task, gboolean top) { char *key = NULL; char *key_complete = NULL; const char *task_s = task2text(task); if(top) { key = generate_op_key(rsc->id, task_s, 0); key_complete = generate_op_key(rsc->id, task2text(task+1), 0); } slist_iter(action, action_t, rsc->actions, lpc, if(action->optional == FALSE) { continue; } if(safe_str_eq(action->uuid, key) || safe_str_eq(action->uuid, key_complete)) { crm_debug_3("Marking top-level action %s as required", action->uuid); action->optional = FALSE; } if(strstr(action->uuid, task_s)) { - if(safe_str_eq(CRMD_ACTION_NOTIFIED, action->task) - || safe_str_eq(CRMD_ACTION_NOTIFY, action->task)) { + if(safe_str_eq(RSC_NOTIFIED, action->task) + || safe_str_eq(RSC_NOTIFY, action->task)) { crm_debug_3("Marking %s as required", action->uuid); action->optional = FALSE; } } ); slist_iter( child, resource_t, rsc->children, lpc, mark_notifications_required(child, task, FALSE); ); } void clone_expand(resource_t *rsc, pe_working_set_t *data_set) { char *rsc_list = NULL; char *node_list = NULL; char *uuid_list = NULL; notify_data_t *n_data = NULL; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); crm_malloc0(n_data, sizeof(notify_data_t)); n_data->keys = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); crm_debug_2("Processing actions from %s", rsc->id); if(is_set(rsc->flags, pe_rsc_notify)) { slist_iter( child_rsc, resource_t, rsc->children, lpc, slist_iter( op, action_t, rsc->actions, lpc2, child_rsc->cmds->create_notify_element( child_rsc, op, n_data, data_set); ); ); } /* expand the notify data */ if(is_set(rsc->flags, pe_rsc_notify) && n_data->stop) { n_data->stop = g_list_sort( n_data->stop, sort_notify_entries); rsc_list = NULL; node_list = NULL; expand_list(n_data->stop, clone_data->clone_max, &rsc_list, &node_list, &uuid_list); 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(rsc_list != NULL) { mark_notifications_required(rsc, stop_rsc, TRUE); } } if(is_set(rsc->flags, pe_rsc_notify) && n_data->start) { n_data->start = g_list_sort( n_data->start, sort_notify_entries); rsc_list = NULL; node_list = NULL; expand_list(n_data->start, clone_data->clone_max, &rsc_list, &node_list, &uuid_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); mark_notifications_required(rsc, start_rsc, TRUE); } if(is_set(rsc->flags, pe_rsc_notify) && n_data->demote) { n_data->demote = g_list_sort( n_data->demote, sort_notify_entries); rsc_list = NULL; node_list = NULL; expand_list(n_data->demote, clone_data->clone_max, &rsc_list, &node_list, &uuid_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); mark_notifications_required(rsc, action_demote, TRUE); } if(is_set(rsc->flags, pe_rsc_notify) && n_data->promote) { n_data->promote = g_list_sort( n_data->promote, sort_notify_entries); rsc_list = NULL; node_list = NULL; uuid_list = NULL; expand_list(n_data->promote, clone_data->clone_max, &rsc_list, &node_list, &uuid_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); mark_notifications_required(rsc, action_promote, TRUE); } if(is_set(rsc->flags, pe_rsc_notify) && n_data->active) { n_data->active = g_list_sort( n_data->active, sort_notify_entries); rsc_list = NULL; node_list = NULL; uuid_list = NULL; expand_list(n_data->active, clone_data->clone_max, &rsc_list, &node_list, &uuid_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(is_set(rsc->flags, pe_rsc_notify) && n_data->slave) { n_data->slave = g_list_sort( n_data->slave, sort_notify_entries); rsc_list = NULL; node_list = NULL; uuid_list = NULL; expand_list(n_data->slave, clone_data->clone_max, &rsc_list, &node_list, &uuid_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(is_set(rsc->flags, pe_rsc_notify) && n_data->master) { n_data->master = g_list_sort( n_data->master, sort_notify_entries); rsc_list = NULL; node_list = NULL; uuid_list = NULL; expand_list(n_data->master, clone_data->clone_max, &rsc_list, &node_list, &uuid_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(is_set(rsc->flags, pe_rsc_notify) && n_data->inactive) { n_data->inactive = g_list_sort( n_data->inactive, sort_notify_entries); rsc_list = NULL; node_list = NULL; uuid_list = NULL; expand_list(n_data->inactive, clone_data->clone_max, &rsc_list, &node_list, &uuid_list); g_hash_table_insert( n_data->keys, crm_strdup("notify_inactive_resource"), rsc_list); g_hash_table_insert( n_data->keys, crm_strdup("notify_inactive_uname"), node_list); } /* yes, we DO need this second loop */ slist_iter( child_rsc, resource_t, rsc->children, lpc, child_rsc->cmds->expand(child_rsc, data_set); ); /* slist_iter( */ /* action, action_t, rsc->actions, lpc2, */ -/* if(safe_str_eq(action->task, CRMD_ACTION_NOTIFY)) { */ +/* if(safe_str_eq(action->task, RSC_NOTIFY)) { */ /* action->meta_xml = notify_xml; */ /* } */ /* ); */ native_expand(rsc, data_set); /* destroy the notify_data */ 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); } 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, slist_iter(known ,node_t, child->known_on, lpc2, if(node->details == known->details) { return child; } ); ); 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(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; } diff --git a/pengine/constraints.c b/pengine/constraints.c index 9ed717f1f6..4781c90d8a 100644 --- a/pengine/constraints.c +++ b/pengine/constraints.c @@ -1,696 +1,993 @@ /* * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net> * * 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 <crm_internal.h> #include <sys/param.h> #include <crm/crm.h> #include <crm/cib.h> #include <crm/msg_xml.h> #include <crm/common/xml.h> #include <crm/common/msg.h> #include <clplumbing/cl_misc.h> #include <glib.h> #include <crm/pengine/status.h> #include <pengine.h> #include <allocate.h> #include <utils.h> #include <crm/pengine/rules.h> #include <lib/crm/pengine/utils.h> 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(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, CRMD_ACTION_START)) { - return CRMD_ACTION_STOP; + if(safe_str_eq(action, RSC_START)) { + return RSC_STOP; - } else if(safe_str_eq(action, CRMD_ACTION_STOP)) { - return CRMD_ACTION_START; + } else if(safe_str_eq(action, RSC_STOP)) { + return RSC_START; - } else if(safe_str_eq(action, CRMD_ACTION_PROMOTE)) { - return CRMD_ACTION_DEMOTE; + } else if(safe_str_eq(action, RSC_PROMOTE)) { + return RSC_DEMOTE; - } else if(safe_str_eq(action, CRMD_ACTION_DEMOTE)) { - return CRMD_ACTION_PROMOTE; + } else if(safe_str_eq(action, RSC_DEMOTE)) { + return RSC_PROMOTE; - } else if(safe_str_eq(action, CRMD_ACTION_PROMOTED)) { - return CRMD_ACTION_DEMOTED; + } else if(safe_str_eq(action, RSC_PROMOTED)) { + return RSC_DEMOTED; - } else if(safe_str_eq(action, CRMD_ACTION_DEMOTED)) { - return CRMD_ACTION_PROMOTED; + } else if(safe_str_eq(action, RSC_DEMOTED)) { + return RSC_PROMOTED; - } else if(safe_str_eq(action, CRMD_ACTION_STARTED)) { - return CRMD_ACTION_STOPPED; + } else if(safe_str_eq(action, RSC_STARTED)) { + return RSC_STOPPED; - } else if(safe_str_eq(action, CRMD_ACTION_STOPPED)) { - return CRMD_ACTION_STARTED; + } else if(safe_str_eq(action, RSC_STOPPED)) { + return RSC_STARTED; } crm_config_warn("Unknown action: %s", action); return NULL; } -gboolean -unpack_rsc_order(xmlNode * xml_obj, pe_working_set_t *data_set) +static gboolean +unpack_simple_rsc_order(xmlNode * xml_obj, pe_working_set_t *data_set) { int score_i = 0; int order_id = 0; resource_t *rsc_lh = NULL; resource_t *rsc_rh = NULL; gboolean symmetrical_bool = TRUE; enum pe_ordering cons_weight = pe_order_optional; const char *id_rh = NULL; const char *id_lh = NULL; const char *action = NULL; const char *action_rh = NULL; const char *id = crm_element_value(xml_obj, XML_ATTR_ID); const char *type = crm_element_value(xml_obj, XML_ATTR_TYPE); const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE); const char *symmetrical = crm_element_value( xml_obj, XML_CONS_ATTR_SYMMETRICAL); cl_str_to_boolean(symmetrical, &symmetrical_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_lh = crm_element_value(xml_obj, XML_CONS_ATTR_TO); id_rh = crm_element_value(xml_obj, XML_CONS_ATTR_FROM); action = crm_element_value(xml_obj, XML_CONS_ATTR_ACTION); action_rh = crm_element_value(xml_obj, XML_CONS_ATTR_TOACTION); if(action == NULL) { - action = CRMD_ACTION_START; + action = RSC_START; } if(action_rh == NULL) { action_rh = action; } if(safe_str_neq(type, "before")) { /* normalize the input - swap everything over */ const char *tmp = NULL; type = "before"; tmp = id_rh; id_rh = id_lh; id_lh = tmp; tmp = action_rh; action_rh = action; action = tmp; } if(id_lh == NULL || id_rh == NULL) { crm_config_err("Constraint %s needs two sides lh: %s rh: %s", id, crm_str(id_lh), crm_str(id_rh)); return FALSE; } rsc_lh = pe_find_resource(data_set->resources, id_rh); rsc_rh = pe_find_resource(data_set->resources, id_lh); if(rsc_lh == NULL) { crm_config_err("Constraint %s: no resource found for LHS (%s)", id, id_rh); return FALSE; } else if(rsc_rh == NULL) { crm_config_err("Constraint %s: no resource found for RHS of (%s)", id, id_lh); return FALSE; } if(score == NULL) { score = "INFINITY"; } score_i = char2score(score); cons_weight = pe_order_optional; if(score_i == 0 && rsc_rh->restart_type == pe_restart_restart) { crm_debug_2("Upgrade : recovery - implies right"); cons_weight |= pe_order_implies_right; } if(score_i < 0) { crm_debug_2("Upgrade : implies left"); cons_weight |= pe_order_implies_left; } else if(score_i > 0) { crm_debug_2("Upgrade : implies right"); cons_weight |= pe_order_implies_right; - if(safe_str_eq(action, CRMD_ACTION_START) - || safe_str_eq(action, CRMD_ACTION_PROMOTE)) { + if(safe_str_eq(action, RSC_START) + || safe_str_eq(action, RSC_PROMOTE)) { crm_debug_2("Upgrade : runnable"); cons_weight |= pe_order_runnable_left; } } - order_id = custom_action_order( - rsc_lh, generate_op_key(rsc_lh->id, action, 0), NULL, - rsc_rh, generate_op_key(rsc_rh->id, action_rh, 0), NULL, - cons_weight, data_set); + order_id = new_rsc_order(rsc_lh, action, rsc_rh, action_rh, cons_weight, data_set); crm_debug_2("order-%d (%s): %s_%s %s %s_%s flags=0x%.6x", order_id, id, rsc_lh->id, action, type, rsc_rh->id, action_rh, cons_weight); if(symmetrical_bool == FALSE) { return TRUE; } action = invert_action(action); action_rh = invert_action(action_rh); cons_weight = pe_order_optional; if(score_i == 0 && rsc_rh->restart_type == pe_restart_restart) { crm_debug_2("Upgrade : recovery - implies left"); cons_weight |= pe_order_implies_left; } score_i *= -1; if(score_i < 0) { crm_debug_2("Upgrade : implies left"); cons_weight |= pe_order_implies_left; - if(safe_str_eq(action_rh, CRMD_ACTION_DEMOTE)) { + if(safe_str_eq(action_rh, RSC_DEMOTE)) { crm_debug_2("Upgrade : demote"); cons_weight |= pe_order_demote; } } else if(score_i > 0) { crm_debug_2("Upgrade : implies right"); cons_weight |= pe_order_implies_right; - if(safe_str_eq(action, CRMD_ACTION_START) - || safe_str_eq(action, CRMD_ACTION_PROMOTE)) { + if(safe_str_eq(action, RSC_START) + || safe_str_eq(action, RSC_PROMOTE)) { crm_debug_2("Upgrade : runnable"); cons_weight |= pe_order_runnable_left; } } if(action == NULL || action_rh == NULL) { crm_config_err("Cannot invert rsc_order constraint %s." " Please specify the inverse manually.", id); return TRUE; } - order_id = custom_action_order( - rsc_rh, generate_op_key(rsc_rh->id, action_rh, 0), NULL, - rsc_lh, generate_op_key(rsc_lh->id, action, 0), NULL, - cons_weight, data_set); + order_id = new_rsc_order( + rsc_rh, action_rh, rsc_lh, action, cons_weight, data_set); crm_debug_2("order-%d (%s): %s_%s %s %s_%s flags=0x%.6x", order_id, id, rsc_rh->id, action_rh, type, rsc_lh->id, action, cons_weight); return TRUE; } gboolean unpack_rsc_location(xmlNode * xml_obj, pe_working_set_t *data_set) { gboolean empty = TRUE; 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); 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; } else if(is_not_set(rsc_lh->flags, pe_rsc_managed)) { crm_debug_2("Ignoring constraint %s: resource %s not managed", id, id_lh); return FALSE; } if(node != NULL && score != NULL) { int score_i = char2score(score); node_t *match = pe_find_node(data_set->nodes, node); if(match) { rsc2node_new(id, rsc_lh, score_i, match, data_set); return TRUE; } else { return FALSE; } } 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)); } 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_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(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); ); } xml_child_iter( rule_xml, expr, enum expression_type type = find_expression_type(expr); crm_debug_2("Processing expression: %s", ID(expr)); if(type == not_expr) { pe_err("Expression <%s id=%s...> is not valid", crm_element_name(expr), crm_str(ID(expr))); continue; } slist_iter( node, node_t, data_set->nodes, lpc, if(type == nested_rule) { accept = test_rule( expr, node->details->attrs, RSC_ROLE_UNKNOWN, data_set->now); } else { accept = test_expression( expr, node->details->attrs, RSC_ROLE_UNKNOWN, data_set->now); } 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; crm_debug_4("%s ==> %s (%d)", rsc_lh->id, rsc_rh->id, 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; } +static enum pe_ordering get_flags( + const char *id, int score, const char *action_1, const char *action_2) { + enum pe_ordering flags = pe_order_optional; + + if(score < 0) { + crm_debug_2("Upgrade %s: implies left", id); + flags |= pe_order_implies_left; + if(safe_str_eq(action_2, RSC_DEMOTE)) { + crm_debug_2("Upgrade %s: demote", id); + flags |= pe_order_demote; + } + + } else if(score > 0) { + crm_debug_2("Upgrade %s: implies right", id); + flags |= pe_order_implies_right; + if(safe_str_eq(action_1, RSC_START) + || safe_str_eq(action_1, RSC_PROMOTE)) { + crm_debug_2("Upgrade %s: runnable", id); + flags |= pe_order_runnable_left; + } + } + return flags; +} + + +static gboolean +unpack_order_set(xmlNode *set, int score, pe_working_set_t *data_set) +{ + resource_t *last = NULL; + resource_t *resource = NULL; + const char *action = crm_element_value(set, "action"); + const char *sequential = crm_element_value(set, "sequential"); + const char *score_s = crm_element_value(set, XML_RULE_ATTR_SCORE); + int local_score = score; + enum pe_ordering flags = pe_order_optional; + + + if(score_s) { + local_score = char2score(score_s); + } + + flags = get_flags(ID(set), local_score, action, action); + + if(crm_is_true(sequential) == FALSE) { + xml_child_iter_filter( + set, xml_rsc, "resource", + + resource = pe_find_resource(data_set->resources, ID(xml_rsc)); + if(last != NULL) { + new_rsc_order(last, action, resource, action, flags, data_set); + } + + last = resource; + ); + } + + return TRUE; +} + +gboolean +unpack_rsc_order(xmlNode *xml_obj, pe_working_set_t *data_set) +{ + int score_i = 0; + xmlNode *set1 = NULL; + xmlNode *set2 = NULL; + + const char *id = crm_element_value(xml_obj, XML_ATTR_ID); + const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE); + + xml_child_iter_filter( + xml_obj, set, "resource_set", + if(set1 == NULL) { + set1 = set; + } else if(set2 == NULL) { + set2 = set; + } else { + crm_config_err("A maximum of two resource sets are allowed in constraint %s", id); + } + ); + + if(set1 == NULL) { + return unpack_simple_rsc_order(xml_obj, data_set); + } + + if(score == NULL) { + score = "INFINITY"; + } + + score_i = char2score(score); + if(unpack_order_set(set1, score_i, data_set) == FALSE) { + return FALSE; + } + + if(set2 != NULL) { + 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(xml_obj), score_i, action_1, action_2); + + if(unpack_order_set(set2, score_i, data_set) == FALSE) { + return FALSE; + } + + if(crm_is_true(sequential_1)) { + /* get the first one */ + xml_child_iter_filter( + set1, xml_rsc, "resource", + 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, "resource", + 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, "resource", + 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, "resource", + 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, "resource", + rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc)); + + xml_child_iter_filter( + set2, xml_rsc_2, "resource", + 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 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(crm_is_true(sequential) == FALSE) { + xml_child_iter_filter( + set, xml_rsc, "resource", + + resource = pe_find_resource(data_set->resources, ID(xml_rsc)); + if(with != NULL) { + rsc_colocation_new(set_id, NULL, local_score, resource, with, role, role, data_set); + } + + with = resource; + ); + } + + return TRUE; +} + gboolean -unpack_rsc_colocation(xmlNode * xml_obj, pe_working_set_t *data_set) +unpack_rsc_colocation(xmlNode *xml_obj, pe_working_set_t *data_set) { int score_i = 0; + xmlNode *set1 = NULL; + xmlNode *set2 = NULL; + const char *id = crm_element_value(xml_obj, XML_ATTR_ID); - const char *id_rh = crm_element_value(xml_obj, XML_CONS_ATTR_TO); - const char *id_lh = crm_element_value(xml_obj, XML_CONS_ATTR_FROM); const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE); - const char *state_lh = crm_element_value(xml_obj, XML_RULE_ATTR_FROMSTATE); - const char *state_rh = crm_element_value(xml_obj, XML_RULE_ATTR_TOSTATE); - const char *attr = crm_element_value(xml_obj, "node_attribute"); const char *symmetrical = crm_element_value(xml_obj, XML_CONS_ATTR_SYMMETRICAL); + xml_child_iter_filter( + xml_obj, set, "resource_set", + if(set1 == NULL) { + set1 = set; + } else if(set2 == NULL) { + set2 = set; + } else { + crm_config_err("A maximum of two resource sets are allowed in constraint %s", id); + } + ); - 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) { + if(score) { + score_i = char2score(score); + } + + if(set1 == NULL) { + const char *id_rh = crm_element_value(xml_obj, XML_CONS_ATTR_TO); + const char *id_lh = crm_element_value(xml_obj, XML_CONS_ATTR_FROM); + const char *state_lh = crm_element_value(xml_obj, XML_RULE_ATTR_FROMSTATE); + const char *state_rh = crm_element_value(xml_obj, XML_RULE_ATTR_TOSTATE); + const char *attr = crm_element_value(xml_obj, "node_attribute"); + + 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("No resource (con=%s, rsc=%s)", id, id_lh); return FALSE; - } else if(rsc_rh == NULL) { + } else if(rsc_rh == NULL) { crm_config_err("No resource (con=%s, rsc=%s)", id, id_rh); return FALSE; - } - - if(score) { - score_i = char2score(score); - } + } - rsc_colocation_new( - id, attr, score_i, rsc_lh, rsc_rh, state_lh, state_rh, data_set); + rsc_colocation_new( + id, attr, score_i, rsc_lh, rsc_rh, state_lh, state_rh, data_set); - if(crm_is_true(symmetrical)) { + if(crm_is_true(symmetrical)) { rsc_colocation_new( id, attr, score_i, rsc_rh, rsc_lh, state_rh, state_lh, data_set); + } + return TRUE; + } + + if(set1 != NULL) { + if(unpack_colocation_set(set1, score_i, data_set) == FALSE) { + return FALSE; + } } + + if(set2 != NULL) { + 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(unpack_colocation_set(set2, score_i, data_set) == FALSE) { + return FALSE; + } + + if(crm_is_true(sequential_1)) { + /* get the first one */ + xml_child_iter_filter( + set1, xml_rsc, "resource", + 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, "resource", + 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_i, rsc_1, rsc_2, role_1, role_2, data_set); + + } else if(rsc_1 != NULL) { + xml_child_iter_filter( + set2, xml_rsc, "resource", + rsc_2 = pe_find_resource(data_set->resources, ID(xml_rsc)); + rsc_colocation_new(id, NULL, score_i, rsc_1, rsc_2, role_1, role_2, data_set); + ); + + } else if(rsc_2 != NULL) { + xml_child_iter_filter( + set1, xml_rsc, "resource", + rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc)); + rsc_colocation_new(id, NULL, score_i, rsc_1, rsc_2, role_1, role_2, data_set); + ); + + } else { + xml_child_iter_filter( + set1, xml_rsc, "resource", + rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc)); + + xml_child_iter_filter( + set2, xml_rsc_2, "resource", + rsc_2 = pe_find_resource(data_set->resources, ID(xml_rsc_2)); + rsc_colocation_new(id, NULL, score_i, rsc_1, rsc_2, role_1, role_2, data_set); + ); + ); + } + } + return TRUE; } gboolean is_active(rsc_to_node_t *cons) { return TRUE; } diff --git a/pengine/graph.c b/pengine/graph.c index 51558d01cb..1a1f518748 100644 --- a/pengine/graph.c +++ b/pengine/graph.c @@ -1,729 +1,729 @@ /* * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net> * * 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 <crm_internal.h> #include <sys/param.h> #include <crm/crm.h> #include <crm/cib.h> #include <crm/msg_xml.h> #include <crm/common/xml.h> #include <crm/common/msg.h> #include <glib.h> #include <allocate.h> #include <lib/crm/pengine/utils.h> #include <utils.h> gboolean update_action(action_t *action); gboolean update_action_states(GListPtr actions) { crm_debug_2("Updating %d actions", g_list_length(actions)); slist_iter( action, action_t, actions, lpc, update_action(action); ); return TRUE; } gboolean update_action(action_t *action) { int local_type = 0; int default_log_level = LOG_DEBUG_3; int log_level = default_log_level; gboolean changed = FALSE; do_crm_log(log_level, "Processing action %s: %s %s %s", action->uuid, action->optional?"optional":"required", action->runnable?"runnable":"unrunnable", action->pseudo?"pseudo":action->task); slist_iter( other, action_wrapper_t, action->actions_before, lpc, gboolean other_changed = FALSE; node_t *node = other->action->node; resource_t *other_rsc = other->action->rsc; enum rsc_role_e other_role = RSC_ROLE_UNKNOWN; if(other_rsc) { other_role = other_rsc->fns->state(other_rsc, TRUE); } if(other->type & pe_order_test) { log_level = LOG_NOTICE; do_crm_log(log_level, "Processing action %s: %s %s %s", action->uuid, action->optional?"optional":"required", action->runnable?"runnable":"unrunnable", action->pseudo?"pseudo":action->task); } else { log_level = default_log_level; } do_crm_log(log_level, " Checking action %s: %s %s %s (flags=0x%.6x)", other->action->uuid, other->action->optional?"optional":"required", other->action->runnable?"runnable":"unrunnable", other->action->pseudo?"pseudo":other->action->task, other->type); local_type = other->type; if((local_type & pe_order_demote_stop) && other->action->pseudo == FALSE && other_role > RSC_ROLE_SLAVE && node != NULL && node->details->online) { local_type |= pe_order_implies_left; do_crm_log(log_level,"Upgrading demote->stop constraint to implies_left"); } if((local_type & pe_order_demote) && other->action->pseudo == FALSE && other_role > RSC_ROLE_SLAVE && node != NULL && node->details->online) { local_type |= pe_order_runnable_left; do_crm_log(log_level,"Upgrading restart constraint to runnable_left"); } if((local_type & pe_order_complex_right) && action->optional && other->action->optional == FALSE) { local_type |= pe_order_implies_right; do_crm_log(log_level,"Upgrading complex constraint to implies_right"); } if((local_type & pe_order_complex_left) && action->optional == FALSE && other->action->optional) { local_type |= pe_order_implies_left; do_crm_log(log_level,"Upgrading complex constraint to implies_left"); } if((local_type & pe_order_shutdown) && action->optional && other->action->optional == FALSE && is_set(other_rsc->flags, pe_rsc_shutdown)) { action->optional = FALSE; changed = TRUE; do_crm_log(log_level-1, " * Marking action %s manditory because of %s (complex)", action->uuid, other->action->uuid); } if((local_type & pe_order_restart) && other_role > RSC_ROLE_STOPPED) { if(other_rsc->variant == pe_native) { local_type |= pe_order_implies_left; do_crm_log(log_level,"Upgrading restart constraint to implies_left"); } if(other->action->optional && other->action->runnable && action->runnable == FALSE) { do_crm_log(log_level-1, " * Marking action %s manditory because %s is unrunnable", other->action->uuid, action->uuid); other->action->optional = FALSE; set_bit(other_rsc->flags, pe_rsc_shutdown); other_changed = TRUE; } } if((local_type & pe_order_runnable_left) && other->action->runnable == FALSE) { if(other->action->implied_by_stonith) { do_crm_log(log_level, "Ignoring un-runnable - implied_by_stonith"); } else if(action->runnable == FALSE) { do_crm_log(log_level+1, "Already un-runnable"); } else { action->runnable = FALSE; do_crm_log(log_level-1, " * Marking action %s un-runnable because of %s", action->uuid, other->action->uuid); changed = TRUE; } } if((local_type & pe_order_runnable_right) && action->runnable == FALSE) { if(action->pseudo) { do_crm_log(log_level, "Ignoring un-runnable - pseudo"); } else if(other->action->runnable == FALSE) { do_crm_log(log_level+1, "Already un-runnable"); } else { other->action->runnable = FALSE; do_crm_log(log_level-1, " * Marking action %s un-runnable because of %s", other->action->uuid, action->uuid); other_changed = TRUE; } } if(local_type & pe_order_implies_left) { if(other->action->optional == FALSE) { /* nothing to do */ do_crm_log(log_level+1, " Ignoring implies left - redundant"); - } else if(safe_str_eq(other->action->task, CRMD_ACTION_STOP) + } else if(safe_str_eq(other->action->task, RSC_STOP) && other_role == RSC_ROLE_STOPPED) { do_crm_log(log_level-1, " Ignoring implies left - %s already stopped", other_rsc->id); } else if((local_type & pe_order_demote) && other_rsc->role < RSC_ROLE_MASTER) { do_crm_log(log_level-1, " Ignoring implies left - %s already demoted", other_rsc->id); } else if(action->optional == FALSE) { other->action->optional = FALSE; do_crm_log(log_level-1, " * (implies left) Marking action %s mandatory because of %s", other->action->uuid, action->uuid); other_changed = TRUE; } else { do_crm_log(log_level, " Ignoring implies left"); } } if(local_type & pe_order_implies_left_printed) { if(other->action->optional == TRUE && other->action->print_always == FALSE) { if(action->optional == FALSE || (other->action->pseudo && action->print_always)) { other_changed = TRUE; other->action->print_always = TRUE; do_crm_log(log_level-1, " * (implies left) Ensuring action %s is included because of %s", other->action->uuid, action->uuid); } } } if(local_type & pe_order_implies_right) { if(action->optional == FALSE) { /* nothing to do */ do_crm_log(log_level+1, " Ignoring implies right - redundant"); } else if(other->action->optional == FALSE) { action->optional = FALSE; do_crm_log(log_level-1, " * (implies right) Marking action %s mandatory because of %s", action->uuid, other->action->uuid); changed = TRUE; } else { do_crm_log(log_level, " Ignoring implies right"); } } if(local_type & pe_order_implies_right_printed) { if(action->optional == TRUE && action->print_always == FALSE) { if(other->action->optional == FALSE || (action->pseudo && other->action->print_always)) { changed = TRUE; action->print_always = TRUE; do_crm_log(log_level-1, " * (implies right) Ensuring action %s is included because of %s", action->uuid, other->action->uuid); } } } if(other_changed) { do_crm_log(log_level, "%s changed, processing after list", other->action->uuid); update_action(other->action); slist_iter( before_other, action_wrapper_t, other->action->actions_after, lpc2, do_crm_log(log_level, "%s changed, processing %s", other->action->uuid, before_other->action->uuid); update_action(before_other->action); ); slist_iter( before_other, action_wrapper_t, other->action->actions_before, lpc2, do_crm_log(log_level, "%s changed, processing %s", other->action->uuid, before_other->action->uuid); update_action(before_other->action); ); } ); if(changed) { update_action(action); do_crm_log(log_level, "%s changed, processing after list", action->uuid); slist_iter( other, action_wrapper_t, action->actions_after, lpc, do_crm_log(log_level, "%s changed, processing %s", action->uuid, other->action->uuid); update_action(other->action); ); do_crm_log(log_level, "%s changed, processing before list", action->uuid); slist_iter( other, action_wrapper_t, action->actions_before, lpc, do_crm_log(log_level, "%s changed, processing %s", action->uuid, other->action->uuid); update_action(other->action); ); } return FALSE; } gboolean shutdown_constraints( node_t *node, action_t *shutdown_op, pe_working_set_t *data_set) { /* add the stop to the before lists so it counts as a pre-req * for the shutdown */ slist_iter( rsc, resource_t, node->details->running_rsc, lpc, if(is_not_set(rsc->flags, pe_rsc_managed)) { continue; } custom_action_order( rsc, stop_key(rsc), NULL, NULL, crm_strdup(CRM_OP_SHUTDOWN), shutdown_op, pe_order_implies_left, data_set); ); return TRUE; } gboolean stonith_constraints( node_t *node, action_t *stonith_op, pe_working_set_t *data_set) { CRM_CHECK(stonith_op != NULL, return FALSE); /* * Make sure the stonith OP occurs before we start any shared resources */ if(stonith_op != NULL) { slist_iter( rsc, resource_t, data_set->resources, lpc, rsc->cmds->stonith_ordering(rsc, stonith_op, data_set); ); } /* add the stonith OP as a stop pre-req and the mark the stop * as a pseudo op - since its now redundant */ return TRUE; } static void dup_attr(gpointer key, gpointer value, gpointer user_data) { g_hash_table_replace(user_data, crm_strdup(key), crm_strdup(value)); } xmlNode * action2xml(action_t *action, gboolean as_input) { gboolean needs_node_info = TRUE; xmlNode * action_xml = NULL; xmlNode * args_xml = NULL; char *action_id_s = NULL; if(action == NULL) { return NULL; } crm_debug_4("Dumping action %d as XML", action->id); if(safe_str_eq(action->task, CRM_OP_FENCE)) { action_xml = create_xml_node(NULL, XML_GRAPH_TAG_CRM_EVENT); /* needs_node_info = FALSE; */ } else if(safe_str_eq(action->task, CRM_OP_SHUTDOWN)) { action_xml = create_xml_node(NULL, XML_GRAPH_TAG_CRM_EVENT); } else if(safe_str_eq(action->task, CRM_OP_LRM_REFRESH)) { action_xml = create_xml_node(NULL, XML_GRAPH_TAG_CRM_EVENT); -/* } else if(safe_str_eq(action->task, CRMD_ACTION_PROBED)) { */ +/* } else if(safe_str_eq(action->task, RSC_PROBED)) { */ /* action_xml = create_xml_node(NULL, XML_GRAPH_TAG_CRM_EVENT); */ } else if(action->pseudo) { action_xml = create_xml_node(NULL, XML_GRAPH_TAG_PSEUDO_EVENT); needs_node_info = FALSE; } else { action_xml = create_xml_node(NULL, XML_GRAPH_TAG_RSC_OP); } action_id_s = crm_itoa(action->id); crm_xml_add(action_xml, XML_ATTR_ID, action_id_s); crm_free(action_id_s); crm_xml_add(action_xml, XML_LRM_ATTR_TASK, action->task); if(action->rsc != NULL && action->rsc->clone_name != NULL) { char *clone_key = NULL; const char *interval_s = g_hash_table_lookup(action->meta, "interval"); int interval = crm_parse_int(interval_s, "0"); - if(safe_str_eq(action->task, CRMD_ACTION_NOTIFY)) { + if(safe_str_eq(action->task, RSC_NOTIFY)) { const char *n_type = g_hash_table_lookup( action->extra, crm_meta_name("notify_type")); const char *n_task = g_hash_table_lookup( action->extra, crm_meta_name("notify_operation")); CRM_CHECK(n_type != NULL, ;); CRM_CHECK(n_task != NULL, ;); clone_key = generate_notify_key(action->rsc->clone_name, n_type, n_task); } else { clone_key = generate_op_key(action->rsc->clone_name, action->task, interval); } crm_xml_add(action_xml, XML_LRM_ATTR_TASK_KEY, clone_key); crm_xml_add(action_xml, "internal_"XML_LRM_ATTR_TASK_KEY, action->uuid); crm_free(clone_key); } else { crm_xml_add(action_xml, XML_LRM_ATTR_TASK_KEY, action->uuid); } if(needs_node_info && action->node != NULL) { crm_xml_add(action_xml, XML_LRM_ATTR_TARGET, action->node->details->uname); crm_xml_add(action_xml, XML_LRM_ATTR_TARGET_UUID, action->node->details->id); } if(action->failure_is_fatal == FALSE) { add_hash_param(action->meta, XML_ATTR_TE_ALLOWFAIL, XML_BOOLEAN_TRUE); } if(as_input) { return action_xml; } if(action->notify_keys != NULL) { g_hash_table_foreach( action->notify_keys, dup_attr, action->meta); } if(action->rsc != NULL && action->pseudo == FALSE) { int lpc = 0; xmlNode *rsc_xml = create_xml_node( action_xml, crm_element_name(action->rsc->xml)); const char *attr_list[] = { XML_AGENT_ATTR_CLASS, XML_AGENT_ATTR_PROVIDER, XML_ATTR_TYPE }; if(action->rsc->clone_name != NULL) { crm_debug("Using clone name %s for %s", action->rsc->clone_name, action->rsc->id); crm_xml_add(rsc_xml, XML_ATTR_ID, action->rsc->clone_name); crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->id); } else { crm_xml_add(rsc_xml, XML_ATTR_ID, action->rsc->id); crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->long_name); } for(lpc = 0; lpc < DIMOF(attr_list); lpc++) { crm_xml_add(rsc_xml, attr_list[lpc], g_hash_table_lookup(action->rsc->meta, attr_list[lpc])); } } args_xml = create_xml_node(action_xml, XML_TAG_ATTRS); crm_xml_add(args_xml, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET); g_hash_table_foreach(action->extra, hash2field, args_xml); - if(action->rsc != NULL && safe_str_neq(action->task, CRMD_ACTION_STOP)) { + if(action->rsc != NULL && safe_str_neq(action->task, RSC_STOP)) { g_hash_table_foreach(action->rsc->parameters, hash2field, args_xml); } g_hash_table_foreach(action->meta, hash2metafield, args_xml); if(action->rsc != NULL) { int lpc = 0; const char *key = NULL; const char *value = NULL; const char *meta_list[] = { XML_RSC_ATTR_UNIQUE, XML_RSC_ATTR_INCARNATION, XML_RSC_ATTR_INCARNATION_MAX, XML_RSC_ATTR_INCARNATION_NODEMAX, XML_RSC_ATTR_MASTER_MAX, XML_RSC_ATTR_MASTER_NODEMAX, }; for(lpc = 0; lpc < DIMOF(meta_list); lpc++) { key = meta_list[lpc]; value = g_hash_table_lookup(action->rsc->meta, key); if(value != NULL) { char *crm_name = crm_concat(CRM_META, key, '_'); crm_xml_add(args_xml, crm_name, value); crm_free(crm_name); } } } crm_log_xml_debug_4(action_xml, "dumped action"); return action_xml; } static gboolean should_dump_action(action_t *action) { int log_filter = LOG_DEBUG_5; CRM_CHECK(action != NULL, return FALSE); if(action->dumped) { do_crm_log(log_filter, "action %d (%s) was already dumped", action->id, action->uuid); return FALSE; } else if(action->runnable == FALSE) { do_crm_log(log_filter, "action %d (%s) was not runnable", action->id, action->uuid); return FALSE; } else if(action->optional && action->print_always == FALSE) { do_crm_log(log_filter, "action %d (%s) was optional", action->id, action->uuid); return FALSE; } else if(action->rsc != NULL && is_not_set(action->rsc->flags, pe_rsc_managed)) { const char * interval = NULL; interval = g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL); /* make sure probes go through */ - if(safe_str_neq(action->task, CRMD_ACTION_STATUS)) { + if(safe_str_neq(action->task, RSC_STATUS)) { pe_warn("action %d (%s) was for an unmanaged resource (%s)", action->id, action->uuid, action->rsc->id); return FALSE; } if(interval != NULL && safe_str_neq(interval, "0")) { pe_warn("action %d (%s) was for an unmanaged resource (%s)", action->id, action->uuid, action->rsc->id); return FALSE; } } if(action->pseudo || safe_str_eq(action->task, CRM_OP_FENCE) || safe_str_eq(action->task, CRM_OP_SHUTDOWN)) { /* skip the next checks */ return TRUE; } if(action->node == NULL) { pe_err("action %d (%s) was not allocated", action->id, action->uuid); log_action(LOG_DEBUG, "Unallocated action", action, FALSE); return FALSE; } else if(action->node->details->online == FALSE) { pe_err("action %d was (%s) scheduled for offline node", action->id, action->uuid); log_action(LOG_DEBUG, "Action for offline node", action, FALSE); return FALSE; #if 0 /* but this would also affect resources that can be safely * migrated before a fencing op */ } else if(action->node->details->unclean == FALSE) { pe_err("action %d was (%s) scheduled for unclean node", action->id, action->uuid); log_action(LOG_DEBUG, "Action for unclean node", action, FALSE); return FALSE; #endif } return TRUE; } /* lowest to highest */ static gint sort_action_id(gconstpointer a, gconstpointer b) { const action_wrapper_t *action_wrapper2 = (const action_wrapper_t*)a; const action_wrapper_t *action_wrapper1 = (const action_wrapper_t*)b; if(a == NULL) { return 1; } if(b == NULL) { return -1; } if(action_wrapper1->action->id > action_wrapper2->action->id) { return -1; } if(action_wrapper1->action->id < action_wrapper2->action->id) { return 1; } return 0; } static gboolean should_dump_input(int last_action, action_t *action, action_wrapper_t *wrapper) { int type = wrapper->type; int log_dump = LOG_DEBUG_3; int log_filter = LOG_DEBUG_3; type &= ~pe_order_implies_left_printed; type &= ~pe_order_implies_right_printed; type &= ~pe_order_optional; wrapper->state = pe_link_not_dumped; if(last_action == wrapper->action->id) { do_crm_log(log_filter, "Input (%d) %s duplicated for %s", wrapper->action->id, wrapper->action->uuid, action->uuid); wrapper->state = pe_link_dup; return FALSE; } else if(wrapper->type == pe_order_none) { do_crm_log(log_filter, "Input (%d) %s suppressed for %s", wrapper->action->id, wrapper->action->uuid, action->uuid); return FALSE; } else if(wrapper->action->runnable == FALSE && type == pe_order_none) { do_crm_log(log_filter, "Input (%d) %s optional (ordering) for %s", wrapper->action->id, wrapper->action->uuid, action->uuid); return FALSE; } else if(action->pseudo && (wrapper->type & pe_order_stonith_stop)) { do_crm_log(log_filter, "Input (%d) %s suppressed for %s", wrapper->action->id, wrapper->action->uuid, action->uuid); return FALSE; } else if(wrapper->action->dumped || should_dump_action(wrapper->action)) { do_crm_log(log_dump, "Input (%d) %s should be dumped for %s", wrapper->action->id, wrapper->action->uuid, action->uuid); goto dump; #if 0 } if(wrapper->action->runnable && wrapper->action->pseudo && wrapper->action->rsc->variant != pe_native) { do_crm_log(LOG_CRIT, "Input (%d) %s should be dumped for %s", wrapper->action->id, wrapper->action->uuid, action->uuid); goto dump; #endif } else if(wrapper->action->optional == TRUE && wrapper->action->print_always == FALSE) { do_crm_log(log_filter, "Input (%d) %s optional for %s", wrapper->action->id, wrapper->action->uuid, action->uuid); do_crm_log(log_filter, "Input (%d) %s n=%p p=%d r=%d o=%d a=%d f=0x%.6x", wrapper->action->id, wrapper->action->uuid, wrapper->action->node, wrapper->action->pseudo, wrapper->action->runnable, wrapper->action->optional, wrapper->action->print_always, wrapper->type); return FALSE; } dump: do_crm_log(log_dump, "Input (%d) %s n=%p p=%d r=%d o=%d a=%d f=0x%.6x dumped for %s", wrapper->action->id, wrapper->action->uuid, wrapper->action->node, wrapper->action->pseudo, wrapper->action->runnable, wrapper->action->optional, wrapper->action->print_always, wrapper->type, action->uuid); return TRUE; } void graph_element_from_action(action_t *action, pe_working_set_t *data_set) { int last_action = -1; int synapse_priority = 0; xmlNode * syn = NULL; xmlNode * set = NULL; xmlNode * in = NULL; xmlNode * input = NULL; xmlNode * xml_action = NULL; if(should_dump_action(action) == FALSE) { return; } action->dumped = TRUE; syn = create_xml_node(data_set->graph, "synapse"); set = create_xml_node(syn, "action_set"); in = create_xml_node(syn, "inputs"); crm_xml_add_int(syn, XML_ATTR_ID, data_set->num_synapse); data_set->num_synapse++; if(action->rsc != NULL) { synapse_priority = action->rsc->priority; } if(action->priority > synapse_priority) { synapse_priority = action->priority; } if(synapse_priority > 0) { crm_xml_add_int(syn, XML_CIB_ATTR_PRIORITY, synapse_priority); } xml_action = action2xml(action, FALSE); add_node_nocopy(set, crm_element_name(xml_action), xml_action); action->actions_before = g_list_sort( action->actions_before, sort_action_id); slist_iter(wrapper,action_wrapper_t,action->actions_before,lpc, if(should_dump_input(last_action, action, wrapper) == FALSE) { continue; } wrapper->state = pe_link_dumped; CRM_CHECK(last_action < wrapper->action->id, ;); last_action = wrapper->action->id; input = create_xml_node(in, "trigger"); xml_action = action2xml(wrapper->action, TRUE); add_node_nocopy(input, crm_element_name(xml_action), xml_action); ); } diff --git a/pengine/group.c b/pengine/group.c index 57cfc4b303..ddc81884b0 100644 --- a/pengine/group.c +++ b/pengine/group.c @@ -1,538 +1,509 @@ /* * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net> * * 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 <crm_internal.h> #include <pengine.h> #include <lib/crm/pengine/utils.h> #include <crm/msg_xml.h> #include <clplumbing/cl_misc.h> #include <allocate.h> #include <utils.h> #define VARIANT_GROUP 1 #include <lib/crm/pengine/variant.h> node_t * group_color(resource_t *rsc, pe_working_set_t *data_set) { node_t *node = NULL; node_t *group_node = NULL; group_variant_data_t *group_data = NULL; get_group_variant_data(group_data, rsc); if(is_not_set(rsc->flags, pe_rsc_provisional)) { return rsc->allocated_to; } crm_debug_2("Processing %s", rsc->id); if(is_set(rsc->flags, pe_rsc_allocating)) { crm_debug("Dependancy loop detected involving %s", rsc->id); return NULL; } if(group_data->first_child == NULL) { /* nothign to allocate */ clear_bit(rsc->flags, pe_rsc_provisional); return NULL; } set_bit(rsc->flags, pe_rsc_allocating); rsc->role = group_data->first_child->role; group_data->first_child->rsc_cons = g_list_concat( group_data->first_child->rsc_cons, rsc->rsc_cons); rsc->rsc_cons = NULL; dump_node_scores(scores_log_level, rsc, __PRETTY_FUNCTION__, rsc->allowed_nodes); slist_iter( child_rsc, resource_t, rsc->children, lpc, node = child_rsc->cmds->color(child_rsc, data_set); if(group_node == NULL) { group_node = node; } ); rsc->next_role = group_data->first_child->next_role; clear_bit(rsc->flags, pe_rsc_allocating); clear_bit(rsc->flags, pe_rsc_provisional); if(group_data->colocated) { return group_node; } return NULL; } void group_update_pseudo_status(resource_t *parent, resource_t *child); void group_create_actions(resource_t *rsc, pe_working_set_t *data_set) { action_t *op = NULL; const char *value = NULL; group_variant_data_t *group_data = NULL; get_group_variant_data(group_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); group_update_pseudo_status(rsc, child_rsc); ); op = start_action(rsc, NULL, TRUE/* !group_data->child_starting */); op->pseudo = TRUE; op->runnable = TRUE; op = custom_action(rsc, started_key(rsc), - CRMD_ACTION_STARTED, NULL, + RSC_STARTED, NULL, TRUE/* !group_data->child_starting */, TRUE, data_set); op->pseudo = TRUE; op->runnable = TRUE; op = stop_action(rsc, NULL, TRUE/* !group_data->child_stopping */); op->pseudo = TRUE; op->runnable = TRUE; op = custom_action(rsc, stopped_key(rsc), - CRMD_ACTION_STOPPED, NULL, + RSC_STOPPED, NULL, TRUE/* !group_data->child_stopping */, TRUE, data_set); op->pseudo = TRUE; op->runnable = TRUE; value = g_hash_table_lookup(rsc->parameters, crm_meta_name("stateful")); if(crm_is_true(value)) { - op = custom_action(rsc, demote_key(rsc), CRMD_ACTION_DEMOTE, NULL, TRUE, TRUE, data_set); + op = custom_action(rsc, demote_key(rsc), RSC_DEMOTE, NULL, TRUE, TRUE, data_set); op->pseudo = TRUE; op->runnable = TRUE; - op = custom_action(rsc, demoted_key(rsc), CRMD_ACTION_DEMOTED, NULL, TRUE, TRUE, data_set); + op = custom_action(rsc, demoted_key(rsc), RSC_DEMOTED, NULL, TRUE, TRUE, data_set); op->pseudo = TRUE; op->runnable = TRUE; - op = custom_action(rsc, promote_key(rsc), CRMD_ACTION_PROMOTE, NULL, TRUE, TRUE, data_set); + op = custom_action(rsc, promote_key(rsc), RSC_PROMOTE, NULL, TRUE, TRUE, data_set); op->pseudo = TRUE; op->runnable = TRUE; - op = custom_action(rsc, promoted_key(rsc), CRMD_ACTION_PROMOTED, NULL, TRUE, TRUE, data_set); + op = custom_action(rsc, promoted_key(rsc), RSC_PROMOTED, NULL, TRUE, TRUE, data_set); op->pseudo = TRUE; op->runnable = TRUE; } rsc->actions = rsc->actions; /* rsc->actions = NULL; */ } void group_update_pseudo_status(resource_t *parent, resource_t *child) { group_variant_data_t *group_data = NULL; get_group_variant_data(group_data, parent); if(group_data->ordered == FALSE) { /* If this group is not ordered, then leave the meta-actions as optional */ return; } if(group_data->child_stopping && group_data->child_starting) { return; } slist_iter( action, action_t, child->actions, lpc, if(action->optional) { continue; } - if(safe_str_eq(CRMD_ACTION_STOP, action->task) && action->runnable) { + if(safe_str_eq(RSC_STOP, action->task) && action->runnable) { group_data->child_stopping = TRUE; crm_debug_3("Based on %s the group is stopping", action->uuid); - } else if(safe_str_eq(CRMD_ACTION_START, action->task) && action->runnable) { + } else if(safe_str_eq(RSC_START, action->task) && action->runnable) { group_data->child_starting = TRUE; crm_debug_3("Based on %s the group is starting", action->uuid); } ); } void group_internal_constraints(resource_t *rsc, pe_working_set_t *data_set) { const char *value = NULL; gboolean stateful = FALSE; resource_t *last_rsc = NULL; group_variant_data_t *group_data = NULL; get_group_variant_data(group_data, rsc); native_internal_constraints(rsc, data_set); value = g_hash_table_lookup(rsc->parameters, crm_meta_name("stateful")); stateful = crm_is_true(value); - custom_action_order( - rsc, stopped_key(rsc), NULL, - rsc, start_key(rsc), NULL, - pe_order_optional, data_set); - - custom_action_order( - rsc, stop_key(rsc), NULL, - rsc, stopped_key(rsc), NULL, - pe_order_runnable_left|pe_order_implies_right|pe_order_implies_left, data_set); - - custom_action_order( - rsc, start_key(rsc), NULL, - rsc, started_key(rsc), NULL, - pe_order_runnable_left, data_set); + new_rsc_order(rsc, RSC_STOPPED, rsc, RSC_START, + pe_order_optional, data_set); + + new_rsc_order(rsc, RSC_STOP, rsc, RSC_STOPPED, + pe_order_runnable_left|pe_order_implies_right|pe_order_implies_left, data_set); + + new_rsc_order(rsc, RSC_START, rsc, RSC_STARTED, + pe_order_runnable_left, data_set); slist_iter( child_rsc, resource_t, rsc->children, lpc, int stop = pe_order_shutdown|pe_order_implies_right; int stopped = pe_order_implies_right_printed; int start = pe_order_implies_right|pe_order_runnable_left; int started = pe_order_runnable_left|pe_order_implies_right|pe_order_implies_right_printed; child_rsc->cmds->internal_constraints(child_rsc, data_set); if(last_rsc == NULL) { stop |= pe_order_implies_left; stopped = pe_order_implies_right; } else if(group_data->colocated) { rsc_colocation_new( "group:internal_colocation", NULL, INFINITY, child_rsc, last_rsc, NULL, NULL, data_set); } if(stateful) { - custom_action_order(rsc, demote_key(rsc), NULL, - child_rsc, demote_key(child_rsc), NULL, - stop|pe_order_implies_left_printed, data_set); + new_rsc_order(rsc, RSC_DEMOTE, child_rsc, RSC_DEMOTE, + stop|pe_order_implies_left_printed, data_set); - custom_action_order(child_rsc, demote_key(child_rsc), NULL, - rsc, demoted_key(rsc), NULL, - stopped, data_set); + new_rsc_order(child_rsc, RSC_DEMOTE, rsc, RSC_DEMOTED, stopped, data_set); - custom_action_order(child_rsc, promote_key(child_rsc), NULL, - rsc, promoted_key(rsc), NULL, - started, data_set); + new_rsc_order(child_rsc, RSC_PROMOTE, rsc, RSC_PROMOTED, started, data_set); - custom_action_order(rsc, promote_key(rsc), NULL, - child_rsc, promote_key(child_rsc), NULL, - pe_order_implies_left_printed, data_set); + new_rsc_order(rsc, RSC_PROMOTE, child_rsc, RSC_PROMOTE, + pe_order_implies_left_printed, data_set); } order_start_start(rsc, child_rsc, pe_order_implies_left_printed); order_stop_stop(rsc, child_rsc, stop|pe_order_implies_left_printed); - custom_action_order(child_rsc, stop_key(child_rsc), NULL, - rsc, stopped_key(rsc), NULL, - stopped, data_set); + new_rsc_order(child_rsc, RSC_STOP, rsc, RSC_STOPPED, stopped, data_set); - custom_action_order(child_rsc, start_key(child_rsc), NULL, - rsc, started_key(rsc), NULL, - started, data_set); + new_rsc_order(child_rsc, RSC_START, rsc, RSC_STARTED, started, data_set); if(group_data->ordered == FALSE) { order_start_start(rsc, child_rsc, start|pe_order_implies_left_printed); if(stateful) { - custom_action_order(rsc, promote_key(rsc), NULL, - child_rsc, promote_key(child_rsc), NULL, - start|pe_order_implies_left_printed, data_set); + new_rsc_order(rsc, RSC_PROMOTE, child_rsc, RSC_PROMOTE, + start|pe_order_implies_left_printed, data_set); } } else if(last_rsc != NULL) { child_rsc->restart_type = pe_restart_restart; order_start_start(last_rsc, child_rsc, start); order_stop_stop(child_rsc, last_rsc, pe_order_implies_left); if(stateful) { - custom_action_order(last_rsc, promote_key(last_rsc), NULL, - child_rsc, promote_key(child_rsc), NULL, - start, data_set); - custom_action_order(child_rsc, demote_key(child_rsc), NULL, - last_rsc, demote_key(last_rsc), NULL, - pe_order_implies_left, data_set); + new_rsc_order(last_rsc, RSC_PROMOTE, child_rsc, RSC_PROMOTE, start, data_set); + new_rsc_order(child_rsc, RSC_DEMOTE, last_rsc, RSC_DEMOTE, pe_order_implies_left, data_set); } } else { /* If anyone in the group is starting, then * pe_order_implies_right will cause _everyone_ in the group * to be sent a start action * But this is safe since starting something that is already * started is required to be "safe" */ int flags = pe_order_implies_left|pe_order_implies_right|pe_order_runnable_right|pe_order_runnable_left; order_start_start(rsc, child_rsc, flags); if(stateful) { - custom_action_order(rsc, promote_key(rsc), NULL, - child_rsc, promote_key(child_rsc), NULL, - flags, data_set); + new_rsc_order(rsc, RSC_PROMOTE, child_rsc, RSC_PROMOTE, flags, data_set); } } last_rsc = child_rsc; ); if(group_data->ordered && last_rsc != NULL) { int stop_stop_flags = pe_order_implies_right; int stop_stopped_flags = pe_order_implies_left; order_stop_stop(rsc, last_rsc, stop_stop_flags); - custom_action_order(last_rsc, stop_key(last_rsc), NULL, - rsc, stopped_key(rsc), NULL, - stop_stopped_flags, data_set); + new_rsc_order(last_rsc, RSC_STOP, rsc, RSC_STOPPED, stop_stopped_flags, data_set); if(stateful) { - custom_action_order(rsc, demote_key(rsc), NULL, - last_rsc, demote_key(last_rsc), NULL, - stop_stop_flags, data_set); - custom_action_order(last_rsc, demote_key(last_rsc), NULL, - rsc, demoted_key(rsc), NULL, - stop_stopped_flags, data_set); + new_rsc_order(rsc, RSC_DEMOTE, last_rsc, RSC_DEMOTE, stop_stop_flags, data_set); + new_rsc_order(last_rsc, RSC_DEMOTE, rsc, RSC_DEMOTED, stop_stopped_flags, data_set); } } } void group_rsc_colocation_lh( resource_t *rsc_lh, resource_t *rsc_rh, rsc_colocation_t *constraint) { group_variant_data_t *group_data = NULL; if(rsc_lh == NULL) { pe_err("rsc_lh was NULL for %s", constraint->id); return; } else if(rsc_rh == NULL) { pe_err("rsc_rh was NULL for %s", constraint->id); return; } crm_debug_4("Processing constraints from %s", rsc_lh->id); get_group_variant_data(group_data, rsc_lh); if(group_data->colocated) { group_data->first_child->cmds->rsc_colocation_lh( group_data->first_child, rsc_rh, constraint); return; } else if(constraint->score >= INFINITY) { crm_config_err("%s: Cannot perform manditory colocation" " between non-colocated group and %s", rsc_lh->id, rsc_rh->id); return; } slist_iter( child_rsc, resource_t, rsc_lh->children, lpc, child_rsc->cmds->rsc_colocation_lh( child_rsc, rsc_rh, constraint); ); } void group_rsc_colocation_rh( resource_t *rsc_lh, resource_t *rsc_rh, rsc_colocation_t *constraint) { group_variant_data_t *group_data = NULL; get_group_variant_data(group_data, rsc_rh); CRM_CHECK(rsc_lh->variant == pe_native, return); crm_debug_3("Processing RH of constraint %s", constraint->id); print_resource(LOG_DEBUG_3, "LHS", rsc_lh, TRUE); if(is_set(rsc_rh->flags, pe_rsc_provisional)) { return; } else if(group_data->colocated && group_data->first_child) { group_data->first_child->cmds->rsc_colocation_rh( rsc_lh, group_data->first_child, constraint); return; } else if(constraint->score >= INFINITY) { crm_config_err("%s: Cannot perform manditory colocation with" " non-colocated group: %s", rsc_lh->id, rsc_rh->id); return; } slist_iter( child_rsc, resource_t, rsc_rh->children, lpc, child_rsc->cmds->rsc_colocation_rh( rsc_lh, child_rsc, constraint); ); } void group_rsc_order_lh(resource_t *rsc, order_constraint_t *order, pe_working_set_t *data_set) { group_variant_data_t *group_data = NULL; get_group_variant_data(group_data, rsc); crm_debug_2("%s->%s", order->lh_action_task, order->rh_action_task); if(order->rh_rsc != NULL && (rsc == order->rh_rsc || rsc == order->rh_rsc->parent)) { native_rsc_order_lh(rsc, order, data_set); return; } #if 0 if(order->type != pe_order_optional) { native_rsc_order_lh(rsc, order, data_set); } if(order->type & pe_order_implies_left) { native_rsc_order_lh(group_data->first_child, order, data_set); } #endif convert_non_atomic_task(rsc, order, TRUE); native_rsc_order_lh(rsc, order, data_set); } void group_rsc_order_rh( action_t *lh_action, resource_t *rsc, order_constraint_t *order) { group_variant_data_t *group_data = NULL; get_group_variant_data(group_data, rsc); crm_debug_2("%s->%s", lh_action->uuid, order->rh_action_task); if(rsc == NULL) { return; } 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_action->rsc != NULL && lh_action->rsc != rsc && lh_action->rsc != rsc->parent && lh_action->rsc->parent != rsc) { char *tmp = NULL; char *task_s = NULL; int interval = 0; enum action_tasks task = 0; parse_op_key(order->lh_action_task, &tmp, &task_s, &interval); task = text2task(task_s); crm_free(task_s); crm_free(tmp); switch(task) { case no_action: case monitor_rsc: case action_notify: case action_notified: case shutdown_crm: case stonith_node: break; case stop_rsc: case stopped_rsc: case action_demote: case action_demoted: order->type |= pe_order_complex_left; break; case start_rsc: case started_rsc: case action_promote: case action_promoted: order->type |= pe_order_complex_right; break; } } native_rsc_order_rh(lh_action, rsc, order); } void group_rsc_location(resource_t *rsc, rsc_to_node_t *constraint) { GListPtr saved = constraint->node_list_rh; GListPtr zero = node_list_dup(constraint->node_list_rh, TRUE, FALSE); gboolean reset_scores = TRUE; group_variant_data_t *group_data = NULL; get_group_variant_data(group_data, rsc); crm_debug("Processing rsc_location %s for %s", constraint->id, rsc->id); slist_iter( child_rsc, resource_t, rsc->children, lpc, child_rsc->cmds->rsc_location(child_rsc, constraint); if(group_data->colocated && reset_scores) { reset_scores = FALSE; constraint->node_list_rh = zero; } ); constraint->node_list_rh = saved; pe_free_shallow_adv(zero, TRUE); } void group_expand(resource_t *rsc, pe_working_set_t *data_set) { group_variant_data_t *group_data = NULL; get_group_variant_data(group_data, rsc); crm_debug_3("Processing actions from %s", rsc->id); CRM_CHECK(rsc != NULL, return); native_expand(rsc, data_set); slist_iter( child_rsc, resource_t, rsc->children, lpc, child_rsc->cmds->expand(child_rsc, data_set); ); } GListPtr group_merge_weights( resource_t *rsc, const char *rhs, GListPtr nodes, int factor, gboolean allow_rollback) { group_variant_data_t *group_data = NULL; get_group_variant_data(group_data, rsc); if(is_set(rsc->flags, pe_rsc_merging)) { crm_debug("Breaking dependancy loop with %s at %s", rsc->id, rhs); return nodes; } else if(is_not_set(rsc->flags, pe_rsc_provisional) || can_run_any(nodes) == FALSE) { return nodes; } set_bit(rsc->flags, pe_rsc_merging); nodes = group_data->first_child->cmds->merge_weights( group_data->first_child, rhs, nodes, factor, allow_rollback); slist_iter( constraint, rsc_colocation_t, rsc->rsc_cons_lhs, lpc, nodes = native_merge_weights( constraint->rsc_lh, rsc->id, nodes, constraint->score/INFINITY, allow_rollback); ); clear_bit(rsc->flags, pe_rsc_merging); return nodes; } diff --git a/pengine/master.c b/pengine/master.c index c84fd42a50..283534e8f0 100644 --- a/pengine/master.c +++ b/pengine/master.c @@ -1,850 +1,803 @@ /* * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net> * * 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 <crm_internal.h> #include <crm/msg_xml.h> #include <allocate.h> #include <lib/crm/pengine/utils.h> #include <utils.h> #define VARIANT_CLONE 1 #include <lib/crm/pengine/variant.h> extern gint sort_clone_instance(gconstpointer a, gconstpointer b); extern void clone_create_notifications( resource_t *rsc, action_t *action, action_t *action_complete, pe_working_set_t *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 */ - custom_action_order( - last, promote_key(last), NULL, - rsc, promoted_key(rsc), NULL, - type, data_set); + new_rsc_order(last, RSC_PROMOTE, rsc, RSC_PROMOTED, type, data_set); } return; } /* child promote before global promoted */ - custom_action_order( - child, promote_key(child), NULL, - rsc, promoted_key(rsc), NULL, - type, data_set); + new_rsc_order(child, RSC_PROMOTE, rsc, RSC_PROMOTED, type, data_set); /* global promote before child promote */ - custom_action_order( - rsc, promote_key(rsc), NULL, - child, promote_key(child), NULL, - type, data_set); + 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); - custom_action_order( - last, promote_key(last), NULL, - child, promote_key(child), NULL, - type, data_set); + 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 */ - custom_action_order( - rsc, demote_key(rsc), NULL, - last, demote_key(last), NULL, - pe_order_implies_left, data_set); + new_rsc_order(rsc, RSC_DEMOTE, last, RSC_DEMOTE, + pe_order_implies_left, data_set); } return; } /* child demote before global demoted */ - custom_action_order( - child, demote_key(child), NULL, - rsc, demoted_key(rsc), NULL, - pe_order_implies_right_printed, data_set); + new_rsc_order(child, RSC_DEMOTE, rsc, RSC_DEMOTED, + pe_order_implies_right_printed, data_set); /* global demote before child demote */ - custom_action_order( - rsc, demote_key(rsc), NULL, - child, demote_key(child), NULL, - pe_order_implies_left_printed, data_set); + 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 */ - custom_action_order(child, demote_key(child), NULL, - last, demote_key(last), NULL, - type, data_set); + 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 */ - custom_action_order( - child, demote_key(child), NULL, - rsc, demoted_key(rsc), NULL, - type, data_set); + 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(CRMD_ACTION_DEMOTE, action->task)) { + } else if(safe_str_eq(RSC_DEMOTE, action->task)) { *demoting = TRUE; - } else if(safe_str_eq(CRMD_ACTION_PROMOTE, action->task)) { + } 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(level, "Child %s of %s can't be promoted", child->id, rsc->id); return NULL; } ); } node = rsc->fns->location(rsc, NULL, FALSE); if(rsc->priority < 0) { do_crm_log(level, "%s cannot be master: preference: %d", rsc->id, rsc->priority); return NULL; } else if(node == NULL) { do_crm_log(level, "%s cannot be master: not allocated", rsc->id); return NULL; } else if(can_run_resources(node) == FALSE) { do_crm_log(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) { return local_node; } else { do_crm_log(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) { 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); } static void master_promotion_order(resource_t *rsc) { 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_lhs, lpc, /* (re-)adds location preferences of resource that wish to be * colocated with the master instance */ if(constraint->role_rh == RSC_ROLE_MASTER) { rsc->allowed_nodes = constraint->rsc_lh->cmds->merge_weights( constraint->rsc_lh, rsc->id, rsc->allowed_nodes, 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(rsc->children, sort_master_instance); } 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->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 0 if(rsc->clone_name) { name = rsc->clone_name; crm_err("%s ::= %s", rsc->id, rsc->clone_name); } #endif 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) a<b?b:a static void apply_master_prefs(resource_t *rsc) { int score, new_score; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); if(clone_data->applied_master_prefs) { /* Make sure we only do this once */ return; } clone_data->applied_master_prefs = TRUE; 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); 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(resource_t *rsc, enum rsc_role_e role, gboolean current) { if(current) { if(rsc->variant == pe_native && rsc->running_on != NULL && role > RSC_ROLE_STOPPED) { crm_debug_6("Filtering change %s.role = %s (was %s)", rsc->id, role2text(role), role2text(rsc->role)); } else if(rsc->role != role) { crm_debug_5("Set %s.role = %s (was %s)", rsc->id, role2text(role), role2text(rsc->role)); rsc->role = role; } } else { if(rsc->next_role != role) { crm_debug_5("Set %s.next_role = %s (was %s)", rsc->id, role2text(role), role2text(rsc->next_role)); rsc->next_role = role; if(role == RSC_ROLE_MASTER) { add_hash_param(rsc->parameters, crm_meta_name("role"), role2text(role)); } } } slist_iter( child_rsc, resource_t, rsc->children, lpc, set_role(child_rsc, role, current); ); } 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", child_rsc->id); if(child_rsc->fns->state(child_rsc, TRUE) == RSC_ROLE_STARTED) { set_role(child_rsc, RSC_ROLE_SLAVE, 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: 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: /* the only reason we should be here is if * we're re-creating actions after a stonith */ promoted++; 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); /* mark the first N as masters */ slist_iter( child_rsc, resource_t, rsc->children, lpc, chosen = NULL; crm_debug_2("Processing %s", child_rsc->id); chosen = child_rsc->fns->location(child_rsc, NULL, FALSE); do_crm_log(scores_log_level, "%s promotion score on %s: %d", child_rsc->id, chosen?chosen->details->uname:"none", child_rsc->sort_index); chosen = NULL; /* nuke 'chosen' so that we don't promote more than the * required number of instances */ if(promoted < clone_data->master_max) { chosen = can_be_master(child_rsc); } crm_debug("%s master score: %d", child_rsc->id, child_rsc->priority); if(chosen == NULL) { next_role = child_rsc->fns->state(child_rsc, FALSE); if(next_role == RSC_ROLE_STARTED) { set_role(child_rsc, RSC_ROLE_SLAVE, FALSE); } continue; } chosen->count++; crm_info("Promoting %s", child_rsc->id); set_role(child_rsc, RSC_ROLE_MASTER, FALSE); 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), - CRMD_ACTION_PROMOTED, NULL, !any_promoting, TRUE, data_set); + 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_create_notifications(rsc, action, action_complete, data_set); /* demote */ action = demote_action(rsc, NULL, !any_demoting); action_complete = custom_action( rsc, demoted_key(rsc), - CRMD_ACTION_DEMOTED, NULL, !any_demoting, TRUE, data_set); + 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_create_notifications(rsc, action, action_complete, data_set); /* 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 */ - custom_action_order( - rsc, stopped_key(rsc), NULL, - rsc, start_key(rsc), NULL, - pe_order_optional, data_set); + new_rsc_order(rsc, RSC_STOPPED, rsc, RSC_START, pe_order_optional, data_set); /* global stopped before promote */ - custom_action_order( - rsc, stopped_key(rsc), NULL, - rsc, promote_key(rsc), NULL, - pe_order_optional, data_set); + new_rsc_order(rsc, RSC_STOPPED, rsc, RSC_PROMOTE, pe_order_optional, data_set); /* global demoted before start */ - custom_action_order( - rsc, demoted_key(rsc), NULL, - rsc, start_key(rsc), NULL, - pe_order_optional, data_set); + new_rsc_order(rsc, RSC_DEMOTED, rsc, RSC_START, pe_order_optional, data_set); /* global started before promote */ - custom_action_order( - rsc, started_key(rsc), NULL, - rsc, promote_key(rsc), NULL, - pe_order_optional, data_set); + new_rsc_order(rsc, RSC_STARTED, rsc, RSC_PROMOTE, pe_order_optional, data_set); /* global demoted before stop */ - custom_action_order( - rsc, demoted_key(rsc), NULL, - rsc, stop_key(rsc), NULL, - pe_order_optional, data_set); + new_rsc_order(rsc, RSC_DEMOTED, rsc, RSC_STOP, pe_order_optional, data_set); /* global demote before demoted */ - custom_action_order( - rsc, demote_key(rsc), NULL, - rsc, demoted_key(rsc), NULL, - pe_order_optional, data_set); + new_rsc_order(rsc, RSC_DEMOTE, rsc, RSC_DEMOTED, pe_order_optional, data_set); /* global demoted before promote */ - custom_action_order( - rsc, demoted_key(rsc), NULL, - rsc, promote_key(rsc), NULL, - pe_order_optional, data_set); + 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 */ - custom_action_order( - child_rsc, demote_key(child_rsc), NULL, - child_rsc, promote_key(child_rsc), NULL, - pe_order_optional, data_set); + 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 list1, node_t *other, int score) { node_t *node = NULL; if(other == NULL) { return; } node = (node_t*)pe_find_node_id(list1, other->details->id); if(node != NULL) { 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", child_rsc->id, role2text(next_role)); node_list_update_one(rsc_lh->allowed_nodes, chosen, 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->score > 0 && (constraint->role_lh != RSC_ROLE_MASTER || constraint->role_rh != RSC_ROLE_MASTER)) { rsc_lh->allowed_nodes = node_list_and(lhs, rhs, FALSE); 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; } diff --git a/pengine/native.c b/pengine/native.c index dc4da6055d..db64f07200 100644 --- a/pengine/native.c +++ b/pengine/native.c @@ -1,2017 +1,1999 @@ /* * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net> * * 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 <crm_internal.h> #include <pengine.h> #include <crm/pengine/rules.h> #include <lib/crm/pengine/utils.h> #include <crm/msg_xml.h> #include <allocate.h> #include <utils.h> #define DELETE_THEN_REFRESH 1 /* The crmd will remove the resource from the CIB itself, making this redundant */ #define VARIANT_NATIVE 1 #include <lib/crm/pengine/variant.h> gboolean at_stack_bottom(resource_t *rsc); void node_list_update(GListPtr list1, GListPtr list2, int factor); 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 create_notifications(resource_t *rsc, pe_working_set_t *data_set); 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_pre_notify( resource_t *rsc, node_t *node, action_t *op, notify_data_t *n_data, 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, }, }; static gboolean native_choose_node(resource_t *rsc) { /* 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 */ GListPtr nodes = NULL; node_t *chosen = NULL; 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, g_list_length(rsc->allowed_nodes)); if(rsc->allowed_nodes) { rsc->allowed_nodes = g_list_sort( rsc->allowed_nodes, sort_node_weight); nodes = rsc->allowed_nodes; chosen = g_list_nth_data(nodes, 0); } return native_assign_node(rsc, nodes, chosen); } GListPtr native_merge_weights( resource_t *rsc, const char *rhs, GListPtr nodes, int factor, gboolean allow_rollback) { GListPtr archive = NULL; if(is_set(rsc->flags, pe_rsc_merging)) { crm_debug("%s: Breaking dependancy loop", rhs); return nodes; } else if(is_not_set(rsc->flags, pe_rsc_provisional) || can_run_any(nodes) == FALSE) { return nodes; } set_bit(rsc->flags, pe_rsc_merging); crm_debug_2("%s: Combining scores from %s", rhs, rsc->id); if(allow_rollback) { archive = node_list_dup(nodes, FALSE, FALSE); } node_list_update(nodes, rsc->allowed_nodes, factor); if(archive && can_run_any(nodes) == FALSE) { crm_debug("%s: Rolling back scores from %s", rhs, rsc->id); pe_free_shallow_adv(nodes, TRUE); nodes = archive; goto bail; } pe_free_shallow_adv(archive, TRUE); slist_iter( constraint, rsc_colocation_t, rsc->rsc_cons_lhs, lpc, nodes = constraint->rsc_lh->cmds->merge_weights( constraint->rsc_lh, rhs, nodes, constraint->score/INFINITY, allow_rollback); ); bail: clear_bit(rsc->flags, pe_rsc_merging); return nodes; } 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("Dependancy 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, resource_t *rsc_rh = constraint->rsc_rh; crm_debug_2("%s: Pre-Processing %s (%s)", rsc->id, constraint->id, rsc_rh->id); rsc_rh->cmds->color(rsc_rh, data_set); rsc->cmds->rsc_colocation_lh(rsc, rsc_rh, constraint); ); 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->score/INFINITY, TRUE); ); dump_node_scores(alloc_details-1, rsc, __PRETTY_FUNCTION__, rsc->allowed_nodes); 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, "target_role", data_set); } if(is_set(rsc->flags, pe_rsc_provisional) && native_choose_node(rsc) ) { 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)) { pe_warn("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; int interval_ms = 0; action_t *mon = NULL; gboolean is_optional = TRUE; GListPtr possible_matches = NULL; crm_debug_2("Creating recurring action %s for %s", ID(operation), rsc->id); if(node != NULL) { node_uname = node->details->uname; } interval = crm_element_value(operation, XML_LRM_ATTR_INTERVAL); interval_ms = crm_get_msec(interval); if(interval_ms == 0) { return; } else if(interval_ms < 0) { crm_config_warn("%s contains an invalid interval: %s", ID(operation), interval); return; } value = crm_element_value(operation, "disabled"); if(crm_is_true(value)) { 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(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, CRMD_ACTION_CANCEL, node, + rsc, local_key, RSC_CANCEL, node, FALSE, TRUE, data_set); crm_free(mon->task); - mon->task = crm_strdup(CRMD_ACTION_CANCEL); + 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("%s\t %s", crm_str(node_uname),mon->uuid); } 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) { char *running_master = crm_itoa(EXECRA_RUNNING_MASTER); add_hash_param(mon->meta, XML_ATTR_TE_TARGET_RC, running_master); custom_action_order( rsc, promote_key(rsc), NULL, rsc, NULL, mon, pe_order_optional|pe_order_runnable_left, data_set); crm_free(running_master); } 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) { 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("Creating actions for %s", rsc->id); chosen = rsc->allocated_to; if(chosen != NULL) { CRM_CHECK(rsc->next_role != RSC_ROLE_UNKNOWN, rsc->next_role = RSC_ROLE_STARTED); } unpack_instance_attributes( rsc->xml, XML_TAG_ATTR_SETS, chosen?chosen->details->attrs:NULL, rsc->parameters, NULL, FALSE, data_set->now); crm_debug_2("%s: %s->%s", rsc->id, role2text(rsc->role), role2text(rsc->next_role)); if(g_list_length(rsc->running_on) > 1) { if(rsc->recovery_type == recovery_stop_start) { pe_proc_err("Attempting recovery of resource %s", rsc->id); 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); return; } 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; } if(rsc->next_role != RSC_ROLE_STOPPED && is_set(rsc->flags, pe_rsc_managed)) { 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) { type |= pe_order_restart; } - custom_action_order(rsc, stop_key(rsc), NULL, - rsc, start_key(rsc), NULL, - type, data_set); + new_rsc_order(rsc, RSC_STOP, rsc, RSC_START, type, data_set); - custom_action_order(rsc, demote_key(rsc), NULL, - rsc, stop_key(rsc), NULL, - pe_order_demote_stop, data_set); + new_rsc_order(rsc, RSC_DEMOTE, rsc, RSC_STOP, + pe_order_demote_stop, data_set); - custom_action_order(rsc, start_key(rsc), NULL, - rsc, promote_key(rsc), NULL, - pe_order_runnable_left, data_set); + new_rsc_order(rsc, RSC_START, rsc, RSC_PROMOTE, + pe_order_runnable_left, data_set); - custom_action_order( - rsc, delete_key(rsc), NULL, rsc, start_key(rsc), NULL, - pe_order_optional, data_set); + new_rsc_order(rsc, RSC_DELETE, rsc, RSC_START, + pe_order_optional, data_set); if(is_set(rsc->flags, pe_rsc_notify)) { - char *key1 = NULL; - char *key2 = NULL; - - key1 = generate_op_key(rsc->id, "confirmed-post_notify_start", 0); - key2 = generate_op_key(rsc->id, "pre_notify_promote", 0); - custom_action_order( - rsc, key1, NULL, rsc, key2, NULL, - pe_order_optional, data_set); - - key1 = generate_op_key(rsc->id, "confirmed-post_notify_demote", 0); - key2 = generate_op_key(rsc->id, "pre_notify_stop", 0); - custom_action_order( - rsc, key1, NULL, rsc, key2, NULL, - pe_order_optional, data_set); + new_rsc_order(rsc, "confirmed-post_notify_start", rsc, "pre_notify_promote", + pe_order_optional, data_set); + new_rsc_order(rsc, "confirmed-post_notify_demote", rsc, "pre_notify_stop", + 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); } } 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(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(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(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(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; gboolean do_check = FALSE; const char *attribute = "#id"; 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; } slist_iter( node, node_t, rsc_lh->allowed_nodes, lpc, tmp = g_hash_table_lookup(node->details->attrs, attribute); if(do_check && safe_str_eq(tmp, value)) { 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 = -INFINITY (%s)", constraint->id, rsc_lh->id, node->details->uname, do_check?"failed":"unallocated"); node->weight = -INFINITY; } ); } 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); } } void node_list_update(GListPtr list1, GListPtr list2, int factor) { node_t *other_node = NULL; slist_iter( node, node_t, list1, lpc, if(node == NULL) { continue; } other_node = (node_t*)pe_find_node_id(list2, node->details->id); if(other_node != NULL) { crm_debug_2("%s: %d + %d", node->details->uname, node->weight, other_node->weight); node->weight = merge_weights( factor*other_node->weight, node->weight); } ); } 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; CRM_CHECK(parse_op_key(original_key, &tmp, &task, &interval), crm_err("search key: %s", original_key); return NULL); key = generate_op_key(rsc->id, task, interval); list = find_actions(actions, key, NULL); 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_2("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_2("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, CRMD_ACTION_STOP)) { + && 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 create_notifications(resource_t *rsc, pe_working_set_t *data_set) { } static void register_activity(resource_t *rsc, enum action_tasks task, node_t *node, notify_data_t *n_data) { notify_entry_t *entry = NULL; if(node == NULL) { pe_proc_warn("%s has no node for required action %s", rsc->id, task2text(task)); return; } crm_malloc0(entry, sizeof(notify_entry_t)); entry->rsc = rsc; entry->node = node; 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_err("Unsupported notify action: %s", task2text(task)); crm_free(entry); break; } } static void register_state(resource_t *rsc, node_t *on_node, notify_data_t *n_data) { notify_entry_t *entry = NULL; crm_malloc0(entry, sizeof(notify_entry_t)); entry->rsc = rsc; entry->node = on_node; crm_debug_2("%s state: %s", rsc->id, role2text(rsc->next_role)); switch(rsc->next_role) { case RSC_ROLE_STOPPED: /* n_data->inactive = g_list_append(n_data->inactive, entry); */ crm_free(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; } } void complex_create_notify_element(resource_t *rsc, action_t *op, notify_data_t *n_data, pe_working_set_t *data_set) { node_t *next_node = NULL; gboolean registered = FALSE; char *op_key = NULL; GListPtr possible_matches = NULL; enum action_tasks task = text2task(op->task); if(rsc->children) { slist_iter( child_rsc, resource_t, rsc->children, lpc, child_rsc->cmds->create_notify_element( child_rsc, op, n_data, data_set); ); return; } if(op->pre_notify == NULL || op->post_notify == NULL) { /* no notifications required */ crm_debug_4("No notificaitons required for %s", op->task); return; } next_node = rsc->allocated_to; op_key = generate_op_key(rsc->id, op->task, 0); possible_matches = find_actions(rsc->actions, op_key, NULL); crm_debug_2("Creating notificaitons for: %s (%s->%s)", op->uuid, role2text(rsc->role), role2text(rsc->next_role)); if(rsc->role == rsc->next_role) { register_state(rsc, next_node, n_data); } slist_iter( local_op, action_t, possible_matches, lpc, local_op->notify_keys = n_data->keys; if(local_op->optional == FALSE) { registered = TRUE; register_activity(rsc, task, local_op->node, n_data); } ); /* 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_pre_notify(rsc, current_node, op, n_data, data_set); if(task == action_demote || registered == FALSE) { pe_post_notify(rsc, current_node, op, n_data, data_set); } ); } } /* start / promote */ if(rsc->next_role != RSC_ROLE_STOPPED) { CRM_CHECK(next_node != NULL,;); if(next_node == NULL) { pe_proc_err("next role: %s", role2text(rsc->next_role)); } else if(task == start_rsc || task == action_promote) { if(task != start_rsc || registered == FALSE) { pe_pre_notify(rsc, next_node, op, n_data, data_set); } pe_post_notify(rsc, next_node, op, n_data, data_set); } } crm_free(op_key); g_list_free(possible_matches); } static void dup_attr(gpointer key, gpointer value, gpointer user_data) { char *meta_key = crm_concat(CRM_META, key, '_'); g_hash_table_replace(user_data, meta_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->extra); trigger->notify_keys = n_data->keys; /* 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); value = g_hash_table_lookup(op->meta, "notify_confirm"); if(crm_is_true(value)) { /* notify before pseudo_notified */ crm_debug_3("Ordering %s before %s (%d->%d)", trigger->uuid, confirm->uuid, confirm->id, trigger->id); order_actions(trigger, confirm, pe_order_implies_left); } return trigger; } void pe_pre_notify(resource_t *rsc, node_t *node, action_t *op, notify_data_t *n_data, pe_working_set_t *data_set) { crm_debug_2("%s: %s", rsc->id, op->uuid); pe_notify(rsc, node, op->pre_notify, op->pre_notified, n_data, 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) { action_t *notify = NULL; CRM_CHECK(op != NULL, return); CRM_CHECK(rsc != NULL, return); crm_debug_2("%s: %s", rsc->id, op->uuid); notify = pe_notify(rsc, node, op->post_notify, op->post_notified, n_data, data_set); if(notify != NULL) { /* crm_err("Upgrading priority for %s to INFINITY", notify->uuid); */ notify->priority = INFINITY; } notify = op->post_notified; if(notify != NULL) { 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(notify, mon, pe_order_optional); ); } } 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(is_set(rsc->flags, pe_rsc_failed)) { crm_notice("Recover resource %s\t(%s)", rsc->id, next->details->uname); } else { crm_notice("Move resource %s\t(%s -> %s)", rsc->id, current->details->uname, next->details->uname); } 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)) { action_t *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; } else if(start->optional) { crm_notice("Leave resource %s\t(%s)", rsc->id, next->details->uname); } else { crm_notice("Restart resource %s\t(%s)", rsc->id, next->details->uname); } } } 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(stop->runnable && stop->optional == FALSE) { crm_notice(" %s\tStop %s",current->details->uname,rsc->id); } if(data_set->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) { crm_notice(" %s\tStart %s", next->details->uname, rsc->id); 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); if(optional == FALSE) { crm_notice("%s\tPromote %s", next->details->uname, rsc->id); } 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, crm_notice("%s\tDemote %s", current->details->uname, rsc->id); 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); - custom_action_order( - rsc, stop_key(rsc), NULL, rsc, delete_key(rsc), NULL, - optional?pe_order_implies_right:pe_order_implies_left, data_set); + 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; } gboolean native_create_probe(resource_t *rsc, node_t *node, action_t *complete, gboolean force, pe_working_set_t *data_set) { char *key = NULL; char *target_rc = NULL; action_t *probe = NULL; node_t *running = NULL; CRM_CHECK(node != NULL, 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; } - key = generate_op_key(rsc->id, CRMD_ACTION_STATUS, 0); - probe = custom_action(rsc, key, CRMD_ACTION_STATUS, node, + key = generate_op_key(rsc->id, RSC_STATUS, 0); + probe = custom_action(rsc, key, RSC_STATUS, node, FALSE, TRUE, data_set); probe->optional = FALSE; running = pe_find_node_id(rsc->running_on, node->details->id); if(running == NULL) { target_rc = crm_itoa(EXECRA_NOT_RUNNING); } else if(rsc->role == RSC_ROLE_MASTER) { target_rc = crm_itoa(EXECRA_RUNNING_MASTER); } if(target_rc != NULL) { add_hash_param(probe->meta, XML_ATTR_TE_TARGET_RC, target_rc); crm_free(target_rc); } crm_debug_2("Probing %s on %s (%s)", rsc->id, node->details->uname, role2text(rsc->role)); - - custom_action_order(rsc, NULL, probe, NULL, NULL, complete, - pe_order_implies_right, data_set); + 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_implies_right, data_set); } else { action_t *all_stopped = get_pseudo_op(ALL_STOPPED, data_set); slist_iter(action, action_t, rsc->actions, lpc2, if(action->needs == rsc_req_stonith) { order_actions(all_stopped, action, pe_order_implies_left); } else if(target != NULL - && safe_str_eq(action->task, CRMD_ACTION_START) + && 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_info("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; node_t *node = stonith_op->node; key = stop_key(rsc); action_list = find_actions(rsc->actions, key, 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(node->details->online && 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, node->details->uname); } else { crm_info("%s is implicit after %s is fenced", action->uuid, 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) { order_actions(stonith_op, action, pe_order_optional); } /* 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(A, B) running on nodeX and B.stop has failed, A := stop healthy resource (A.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" dependancy 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). } 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, node); crm_free(key); slist_iter( action, action_t, action_list, lpc2, if(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, 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); } #define ALLOW_WEAK_MIGRATION 0 static gboolean check_stack_element(resource_t *rsc, resource_t *other_rsc, const char *type) { char *key = NULL; int level = LOG_DEBUG; GListPtr action_list = NULL; if(other_rsc == NULL || other_rsc == rsc) { return TRUE; } do_crm_log(level+1, "%s: processing %s (%s)", rsc->id, other_rsc->id, type); if(other_rsc->variant == pe_native) { do_crm_log(level, "%s: depends on %s (mid-stack) %s", rsc->id, other_rsc->id, type); return FALSE; } else if(other_rsc->variant == pe_group) { if(at_stack_bottom(other_rsc) == FALSE) { do_crm_log(level, "%s: depends on group %s (mid-stack) %s", rsc->id, other_rsc->id, type); return FALSE; } return TRUE; } /* is the clone also moving moved around? * * if so, then we can't yet be completely sure the * resource can safely migrate since the node we're * moving too may not have the clone instance started * yet * * in theory we can figure out if the clone instance we * will run on is already there, but there that would * involve too much knowledge of internal clone code. * maybe later... */ do_crm_log(level+1,"%s: start depends on clone %s", rsc->id, other_rsc->id); key = stop_key(other_rsc); action_list = find_actions(other_rsc->actions, key, NULL); crm_free(key); slist_iter( other_stop, action_t, action_list,lpc, if(other_stop && other_stop->optional == FALSE) { do_crm_log(level, "%s: start depends on %s", rsc->id, other_stop->uuid); g_list_free(action_list); return FALSE; } ); g_list_free(action_list); return TRUE; } gboolean at_stack_bottom(resource_t *rsc) { char *key = NULL; action_t *start = NULL; action_t *other = NULL; 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("%s == %s (%d)", rsc->id, target->id, constraint->score); if(constraint->score > 0 && check_stack_element(rsc, target, "coloc") == FALSE) { return FALSE; } ); slist_iter( other_w, action_wrapper_t, start->actions_before, lpc, other = other_w->action; #if ALLOW_WEAK_MIGRATION if((other_w->type & pe_order_implies_right) == 0) { crm_debug_3("%s: depends on %s (optional ordering)", rsc->id, other->uuid); continue; } #endif if(other->optional == FALSE && check_stack_element(rsc, other->rsc, "order") == FALSE) { 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; } do_crm_log(level+1, "Processing %s", rsc->id); CRM_CHECK(rsc->variant == pe_native, return); 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(level+1, "%s: general resource state", rsc->id); return; } key = start_key(rsc); action_list = find_actions(rsc->actions, key, NULL); crm_free(key); if(action_list == NULL) { do_crm_log(level, "%s: no start action", rsc->id); return; } start = action_list->data; g_list_free(action_list); value = g_hash_table_lookup(rsc->meta, "allow_migrate"); if(crm_is_true(value)) { set_bit(rsc->flags, pe_rsc_can_migrate); } if(is_not_set(rsc->flags, pe_rsc_can_migrate) && start->allow_reload_conversion == FALSE) { do_crm_log(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(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(level, "%s: %s", rsc->id, action->task); return; } action = stop; if(action->pseudo || action->optional || action->node == NULL || action->runnable == FALSE) { do_crm_log(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) { crm_notice("Cannot migrate %s from %s to %s" " - %s is not at the bottom of the resource stack", rsc->id, stop->node->details->uname, start->node->details->uname, rsc->id); clear_bit(rsc->flags, pe_rsc_can_migrate); } } if(is_set(rsc->flags, pe_rsc_can_migrate)) { crm_notice("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(CRMD_ACTION_MIGRATE); + 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); /* Hook up to the all_stopped and shutdown actions */ slist_iter( other_w, action_wrapper_t, stop->actions_after, lpc, other = other_w->action; if(other->optional == FALSE && other->rsc == NULL) { order_actions(start, other, other_w->type); } ); slist_iter( other_w, action_wrapper_t, start->actions_before, lpc, other = other_w->action; if(other->optional == FALSE #if ALLOW_WEAK_MIGRATION && (other_w->type & pe_order_implies_right) #endif && other->rsc != NULL && other->rsc != rsc->parent && other->rsc != rsc) { do_crm_log(level, "Ordering %s before %s", other->uuid, stop->uuid); order_actions(other, stop, other_w->type); } ); crm_free(start->uuid); crm_free(start->task); - start->task = crm_strdup(CRMD_ACTION_MIGRATED); + 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); } else if(start->allow_reload_conversion && stop->node->details == start->node->details) { crm_info("Rewriting restart of %s on %s as a reload", rsc->id, start->node->details->uname); crm_free(start->uuid); crm_free(start->task); start->task = crm_strdup("reload"); start->uuid = generate_op_key(rsc->id, start->task, 0); stop->pseudo = TRUE; /* easier than trying to delete it from the graph */ } else { do_crm_log(level+1, "%s nothing to do", rsc->id); } } diff --git a/pengine/pengine.h b/pengine/pengine.h index eca60242e5..3893245b1b 100644 --- a/pengine/pengine.h +++ b/pengine/pengine.h @@ -1,184 +1,174 @@ /* * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net> * * 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 */ #ifndef PENGINE__H #define PENGINE__H #include <clplumbing/ipc.h> typedef struct rsc_to_node_s rsc_to_node_t; typedef struct rsc_colocation_s rsc_colocation_t; typedef struct lrm_agent_s lrm_agent_t; typedef struct order_constraint_s order_constraint_t; #include <glib.h> #include <crm/crm.h> #include <crm/common/msg.h> #include <crm/common/iso8601.h> #include <crm/pengine/rules.h> #include <crm/pengine/common.h> #include <crm/pengine/status.h> #include <crm/pengine/complex.h> enum pe_stop_fail { pesf_block, pesf_stonith, pesf_ignore }; enum pe_ordering { pe_order_none = 0x0, /* deleted */ pe_order_implies_left = 0x01, /* was: _mandatory */ pe_order_implies_right = 0x02, /* was: _recover */ pe_order_runnable_left = 0x10, /* needs the LHS side to be runnable */ pe_order_runnable_right = 0x20, /* needs the RHS side to be runnable */ pe_order_optional = 0x100, /* pure ordering, nothing implied */ pe_order_stonith_stop = 0x200, /* only applies if the action is non-pseudo */ pe_order_restart = 0x400, /* stop-start constraint */ pe_order_demote = 0x800, /* stop-start constraint */ pe_order_shutdown = 0x1000, /* combines with pe_order_restart to make a complex resource shut down */ pe_order_demote_stop = 0x2000, /* upgrades to implies_left if the resource is a master */ pe_order_complex_left = 0x10000, /* upgrades to implies left */ pe_order_complex_right = 0x20000, /* upgrades to implies right */ pe_order_implies_left_printed = 0x40000, /* Like implies left but only ensures the action is printed, not manditory */ pe_order_implies_right_printed = 0x80000, /* Like implies right but only ensures the action is printed, not manditory */ pe_order_test = 0x100000 /* test marker */ }; struct rsc_colocation_s { const char *id; const char *node_attribute; resource_t *rsc_lh; resource_t *rsc_rh; int role_lh; int role_rh; int score; }; struct rsc_to_node_s { const char *id; resource_t *rsc_lh; enum rsc_role_e role_filter; GListPtr node_list_rh; /* node_t* */ }; struct order_constraint_s { int id; enum pe_ordering type; void *lh_opaque; resource_t *lh_rsc; action_t *lh_action; char *lh_action_task; void *rh_opaque; resource_t *rh_rsc; action_t *rh_action; char *rh_action_task; /* (soon to be) variant specific */ /* int lh_rsc_incarnation; */ /* int rh_rsc_incarnation; */ }; enum pe_link_state { pe_link_not_dumped, pe_link_dumped, pe_link_dup, }; typedef struct action_wrapper_s action_wrapper_t; struct action_wrapper_s { enum pe_ordering type; enum pe_link_state state; action_t *action; }; extern gboolean stage0(pe_working_set_t *data_set); extern gboolean probe_resources(pe_working_set_t *data_set); extern gboolean stage2(pe_working_set_t *data_set); extern gboolean stage3(pe_working_set_t *data_set); extern gboolean stage4(pe_working_set_t *data_set); extern gboolean stage5(pe_working_set_t *data_set); extern gboolean stage6(pe_working_set_t *data_set); extern gboolean stage7(pe_working_set_t *data_set); extern gboolean stage8(pe_working_set_t *data_set); extern gboolean summary(GListPtr resources); extern gboolean pe_msg_dispatch(IPC_Channel *sender, void *user_data); extern gboolean process_pe_message( xmlNode *msg, xmlNode *xml_data, IPC_Channel *sender); extern gboolean unpack_constraints( xmlNode *xml_constraints, pe_working_set_t *data_set); extern gboolean update_action_states(GListPtr actions); extern gboolean shutdown_constraints( node_t *node, action_t *shutdown_op, pe_working_set_t *data_set); extern gboolean stonith_constraints( node_t *node, action_t *stonith_op, pe_working_set_t *data_set); extern int custom_action_order( resource_t *lh_rsc, char *lh_task, action_t *lh_action, resource_t *rh_rsc, char *rh_task, action_t *rh_action, enum pe_ordering type, pe_working_set_t *data_set); +extern 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); + #define order_start_start(rsc1,rsc2, type) \ - custom_action_order(rsc1, start_key(rsc1), NULL, \ - rsc2, start_key(rsc2) ,NULL, \ - type, data_set) + new_rsc_order(rsc1, CRMD_ACTION_START, rsc2, CRMD_ACTION_START, type, data_set) #define order_stop_stop(rsc1, rsc2, type) \ - custom_action_order(rsc1, stop_key(rsc1), NULL, \ - rsc2, stop_key(rsc2) ,NULL, \ - type, data_set) - -#define order_stop_start(rsc1, rsc2, type) \ - custom_action_order(rsc1, stop_key(rsc1), NULL, \ - rsc2, start_key(rsc2) ,NULL, \ - type, data_set) - -#define order_start_stop(rsc1, rsc2, type) \ - custom_action_order(rsc1, start_key(rsc1), NULL, \ - rsc2, stop_key(rsc2) ,NULL, \ - type, data_set) + new_rsc_order(rsc1, CRMD_ACTION_STOP, rsc2, CRMD_ACTION_STOP, type, data_set) extern void graph_element_from_action( action_t *action, pe_working_set_t *data_set); extern int scores_log_level; extern const char* transition_idle_timeout; #endif diff --git a/pengine/ptest.c b/pengine/ptest.c index e5456262d7..86b92b88ae 100644 --- a/pengine/ptest.c +++ b/pengine/ptest.c @@ -1,516 +1,516 @@ /* * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net> * * 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 <crm_internal.h> #include <crm/crm.h> #include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <errno.h> #include <fcntl.h> #include <crm/transition.h> #include <crm/common/xml.h> #include <crm/common/util.h> #include <crm/msg_xml.h> #include <crm/cib.h> #define OPTARGS "V?X:D:G:I:Lwxd:aSs" #ifdef HAVE_GETOPT_H # include <getopt.h> #endif #include <glib.h> #include <pengine.h> #include <lib/crm/pengine/utils.h> #include <allocate.h> #if HAVE_LIBXML2 # include <libxml/parser.h> #endif gboolean use_stdin = FALSE; gboolean do_simulation = FALSE; gboolean inhibit_exit = FALSE; gboolean all_actions = FALSE; extern xmlNode * do_calculations( pe_working_set_t *data_set, xmlNode *xml_input, ha_time_t *now); extern void cleanup_calculations(pe_working_set_t *data_set); char *use_date = NULL; FILE *dot_strm = NULL; #define DOT_PREFIX "PE_DOT: " /* #define DOT_PREFIX "" */ #define dot_write(fmt...) if(dot_strm != NULL) { \ fprintf(dot_strm, fmt); \ fprintf(dot_strm, "\n"); \ } else { \ crm_debug(DOT_PREFIX""fmt); \ } static void init_dotfile(void) { dot_write(" digraph \"g\" {"); /* dot_write(" size = \"30,30\""); */ /* dot_write(" graph ["); */ /* dot_write(" fontsize = \"12\""); */ /* dot_write(" fontname = \"Times-Roman\""); */ /* dot_write(" fontcolor = \"black\""); */ /* dot_write(" bb = \"0,0,398.922306,478.927856\""); */ /* dot_write(" color = \"black\""); */ /* dot_write(" ]"); */ /* dot_write(" node ["); */ /* dot_write(" fontsize = \"12\""); */ /* dot_write(" fontname = \"Times-Roman\""); */ /* dot_write(" fontcolor = \"black\""); */ /* dot_write(" shape = \"ellipse\""); */ /* dot_write(" color = \"black\""); */ /* dot_write(" ]"); */ /* dot_write(" edge ["); */ /* dot_write(" fontsize = \"12\""); */ /* dot_write(" fontname = \"Times-Roman\""); */ /* dot_write(" fontcolor = \"black\""); */ /* dot_write(" color = \"black\""); */ /* dot_write(" ]"); */ } static void usage(const char *cli, int exitcode) { FILE *out = exitcode?stderr:stdout; fprintf(out, "Usage: %s -(?|L|X|x) [-V] [-D] [-G] [-I]\n", cli); fprintf(out, " --%s (-%c): This text\n\n", "help", '?'); fprintf(out, " --%s (-%c): Increase verbosity (can be supplied multiple times)\n\n", "verbose", 'V'); fprintf(out, " --%s (-%c): Connect to the CIB and use the current contents as input\n", "live-check", 'L'); fprintf(out, " --%s (-%c): Look for xml on stdin\n", "xml-stream", 'x'); fprintf(out, " --%s (-%c): Display resource allocation scores\n", "show-scores", 's'); fprintf(out, " --%s (-%c)\t<filename> : Look for xml in the named file\n\n", "xml-file", 'X'); fprintf(out, " --%s (-%c)\t<filename> : Save the transition graph to the named file\n", "save-graph", 'G'); fprintf(out, " --%s (-%c)\t<filename> : Save the DOT formatted transition graph to the named file\n", "save-dotfile", 'D'); fprintf(out, " --%s (-%c)\t<filename> : Save the input to the named file\n", "save-input", 'I'); exit(exitcode); } static char * create_action_name(action_t *action) { char *action_name = NULL; const char *action_host = NULL; if(action->node) { action_host = action->node->details->uname; action_name = crm_concat(action->uuid, action_host, ' '); } else if(action->pseudo) { action_name = crm_strdup(action->uuid); } else { action_host = "<none>"; action_name = crm_concat(action->uuid, action_host, ' '); } - if(safe_str_eq(action->task, CRMD_ACTION_CANCEL)) { + if(safe_str_eq(action->task, RSC_CANCEL)) { char *tmp_action_name = action_name; action_name = crm_concat("Cancel", tmp_action_name, ' '); crm_free(tmp_action_name); } return action_name; } gboolean USE_LIVE_CIB = FALSE; int main(int argc, char **argv) { gboolean all_good = TRUE; gboolean show_scores = FALSE; enum transition_status graph_rc = -1; crm_graph_t *transition = NULL; ha_time_t *a_date = NULL; cib_t * cib_conn = NULL; xmlNode * cib_object = NULL; int argerr = 0; int flag; char *msg_buffer = NULL; gboolean optional = FALSE; pe_working_set_t data_set; const char *source = NULL; const char *xml_file = NULL; const char *dot_file = NULL; const char *graph_file = NULL; const char *input_file = NULL; cl_log_set_entity("ptest"); cl_log_set_facility(LOG_USER); set_crm_log_level(LOG_CRIT-1); while (1) { #ifdef HAVE_GETOPT_H int option_index = 0; static struct option long_options[] = { /* Top-level Options */ {"help", 0, 0, '?'}, {"verbose", 0, 0, 'V'}, {"live-check", 0, 0, 'L'}, {"xml-stream", 0, 0, 'x'}, {"show-scores", 0, 0, 's'}, {"xml-file", 1, 0, 'X'}, {"simulate", 0, 0, 'S'}, {"save-graph", 1, 0, 'G'}, {"save-dotfile",1, 0, 'D'}, {"save-input", 1, 0, 'I'}, {0, 0, 0, 0} }; #endif #ifdef HAVE_GETOPT_H flag = getopt_long(argc, argv, OPTARGS, long_options, &option_index); #else flag = getopt(argc, argv, OPTARGS); #endif if (flag == -1) break; switch(flag) { #ifdef HAVE_GETOPT_H case 0: printf("option %s", long_options[option_index].name); if (optarg) printf(" with arg %s", optarg); printf("\n"); break; #endif case 'S': do_simulation = TRUE; break; case 'a': all_actions = TRUE; break; case 'w': inhibit_exit = TRUE; break; case 'x': use_stdin = TRUE; break; case 's': show_scores = TRUE; scores_log_level = crm_log_level; break; case 'X': xml_file = optarg; break; case 'd': use_date = optarg; break; case 'D': dot_file = optarg; break; case 'G': graph_file = optarg; break; case 'I': input_file = optarg; break; case 'V': cl_log_enable_stderr(TRUE); alter_debug(DEBUG_INC); if(show_scores) { scores_log_level = crm_log_level; } break; case 'L': USE_LIVE_CIB = TRUE; break; case '?': usage("ptest", 0); break; default: printf("?? getopt returned character code 0%o ??\n", flag); ++argerr; break; } } if (optind < argc) { printf("non-option ARGV-elements: "); while (optind < argc) { printf("%s ", argv[optind++]); } printf("\n"); } if (optind > argc) { ++argerr; } if (argerr) { crm_err("%d errors in option parsing", argerr); usage("ptest", 1); } crm_info("=#=#=#=#= Getting XML =#=#=#=#="); if(USE_LIVE_CIB) { int rc = cib_ok; source = "live cib"; cib_conn = cib_new(); rc = cib_conn->cmds->signon( cib_conn, "ptest", cib_command_synchronous); if(rc == cib_ok) { crm_info("Reading XML from: live cluster"); cib_object = get_cib_copy(cib_conn); } else { fprintf(stderr, "Live CIB query failed: %s\n", cib_error2string(rc)); return 3; } if(cib_object == NULL) { fprintf(stderr, "Live CIB query failed: empty result\n"); return 3; } } else if(xml_file != NULL) { FILE *xml_strm = fopen(xml_file, "r"); source = xml_file; if(xml_strm == NULL) { cl_perror("Could not open %s for reading", xml_file); } else { if(strstr(xml_file, ".bz2") != NULL) { cib_object = file2xml(xml_strm, TRUE); } else { cib_object = file2xml(xml_strm, FALSE); } fclose(xml_strm); } } else if(use_stdin) { source = "stdin"; cib_object = stdin2xml(); } if(cib_object == NULL && source) { fprintf(stderr, "Could not parse configuration input from: %s\n", source); return 4; } else if(cib_object == NULL) { fprintf(stderr, "Not configuration specified\n"); usage("ptest", 1); } crm_notice("Required feature set: %s", feature_set(cib_object)); do_id_check(cib_object, NULL, FALSE, FALSE); if(!validate_with_dtd(cib_object,FALSE,DTD_DIRECTORY"/crm.dtd")) { crm_crit("%s does not contain a valid configuration", xml_file?xml_file:"<stdin>"); all_good = FALSE; } if(input_file != NULL) { FILE *input_strm = fopen(input_file, "w"); if(input_strm == NULL) { cl_perror("Could not open %s for writing", input_file); } else { msg_buffer = dump_xml_formatted(cib_object); if(fprintf(input_strm, "%s\n", msg_buffer) < 0) { cl_perror("Write to %s failed", input_file); } fflush(input_strm); fclose(input_strm); crm_free(msg_buffer); } } if(use_date != NULL) { a_date = parse_date(&use_date); log_date(LOG_WARNING, "Set fake 'now' to", a_date, ha_log_date|ha_log_time); log_date(LOG_WARNING, "Set fake 'now' to (localtime)", a_date, ha_log_date|ha_log_time|ha_log_local); } do_calculations(&data_set, cib_object, a_date); msg_buffer = dump_xml_formatted(data_set.graph); if(safe_str_eq(graph_file, "-")) { fprintf(stdout, "%s\n", msg_buffer); fflush(stdout); } else if(graph_file != NULL) { FILE *graph_strm = fopen(graph_file, "w"); if(graph_strm == NULL) { cl_perror("Could not open %s for writing", graph_file); } else { if(fprintf(graph_strm, "%s\n", msg_buffer) < 0) { cl_perror("Write to %s failed", graph_file); } fflush(graph_strm); fclose(graph_strm); } } crm_free(msg_buffer); if(dot_file != NULL) { dot_strm = fopen(dot_file, "w"); if(dot_strm == NULL) { cl_perror("Could not open %s for writing", dot_file); } } if(dot_strm == NULL) { goto simulate; } init_dotfile(); slist_iter( action, action_t, data_set.actions, lpc, const char *style = "filled"; const char *font = "black"; const char *color = "black"; const char *fill = NULL; char *action_name = create_action_name(action); crm_debug_3("Action %d: %p", action->id, action); if(action->pseudo) { font = "orange"; } style = "dashed"; if(action->dumped) { style = "bold"; color = "green"; } else if(action->rsc != NULL && is_not_set(action->rsc->flags, pe_rsc_managed)) { color = "purple"; if(all_actions == FALSE) { goto dont_write; } } else if(action->optional) { color = "blue"; if(all_actions == FALSE) { goto dont_write; } } else { color = "red"; CRM_CHECK(action->runnable == FALSE, ;); } action->dumped = TRUE; dot_write("\"%s\" [ style=%s color=\"%s\" fontcolor=\"%s\" %s%s]", action_name, style, color, font, fill?"fillcolor=":"", fill?fill:""); dont_write: crm_free(action_name); ); slist_iter( action, action_t, data_set.actions, lpc, slist_iter( before, action_wrapper_t, action->actions_before, lpc2, char *before_name = NULL; char *after_name = NULL; const char *style = "dashed"; optional = TRUE; if(before->state == pe_link_dumped) { optional = FALSE; style = "bold"; } else if(action->pseudo && (before->type & pe_order_stonith_stop)) { continue; } else if(before->state == pe_link_dup) { continue; } else if(action->dumped && before->action->dumped) { optional = FALSE; } if(all_actions || optional == FALSE) { before_name = create_action_name(before->action); after_name = create_action_name(action); dot_write("\"%s\" -> \"%s\" [ style = %s]", before_name, after_name, style); crm_free(before_name); crm_free(after_name); } ); ); dot_write("}"); if(dot_strm != NULL) { fflush(dot_strm); fclose(dot_strm); } simulate: if(do_simulation == FALSE) { goto cleanup; } transition = unpack_graph(data_set.graph); transition->batch_limit = 0; print_graph(LOG_DEBUG, transition); do { graph_rc = run_graph(transition); } while(graph_rc == transition_active); if(graph_rc != transition_complete) { crm_crit("Transition failed: %s", transition_status(graph_rc)); print_graph(LOG_ERR, transition); } destroy_graph(transition); CRM_CHECK(graph_rc == transition_complete, all_good = FALSE; crm_err("An invalid transition was produced")); cleanup: cleanup_alloc_calculations(&data_set); #if HAVE_LIBXML2 xmlCleanupParser(); #endif /* required for MallocDebug.app */ if(inhibit_exit) { GMainLoop* mainloop = g_main_new(FALSE); g_main_run(mainloop); } if(all_good) { return 0; } return 5; }