diff --git a/cib/messages.c b/cib/messages.c index d96bd08f8e..ec6be13f20 100644 --- a/cib/messages.c +++ b/cib/messages.c @@ -1,462 +1,459 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MAX_DIFF_RETRY 5 #ifdef CIBPIPE gboolean cib_is_master = TRUE; #else gboolean cib_is_master = FALSE; #endif xmlNode *the_cib = NULL; gboolean syncd_once = FALSE; extern const char *cib_our_uname; enum cib_errors revision_check(xmlNode *cib_update, xmlNode *cib_copy, int flags); int get_revision(xmlNode *xml_obj, int cur_revision); enum cib_errors updateList( - xmlNode *local_cib, xmlNode *update_command, xmlNode *failed, - int operation, const char *section); + xmlNode *local_cib, xmlNode *update_command, xmlNode *failed, + int operation, const char *section); gboolean check_generation(xmlNode *newCib, xmlNode *oldCib); gboolean update_results( - xmlNode *failed, xmlNode *target, const char *operation, int return_code); + xmlNode *failed, xmlNode *target, const char *operation, int return_code); enum cib_errors cib_update_counter( - xmlNode *xml_obj, const char *field, gboolean reset); + xmlNode *xml_obj, const char *field, gboolean reset); enum cib_errors sync_our_cib(xmlNode *request, gboolean all); extern xmlNode *cib_msg_copy(const xmlNode *msg, gboolean with_data); extern gboolean cib_shutdown_flag; extern void terminate_cib(const char *caller); enum cib_errors cib_process_shutdown_req( - const char *op, int options, const char *section, xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { #ifdef CIBPIPE return cib_invalid_argument; #else - enum cib_errors result = cib_ok; - const char *host = crm_element_value(req, F_ORIG); + enum cib_errors result = cib_ok; + const char *host = crm_element_value(req, F_ORIG); - *answer = NULL; + *answer = NULL; - if(crm_element_value(req, F_CIB_ISREPLY) == NULL) { - crm_info("Shutdown REQ from %s", host); - return cib_ok; + if(crm_element_value(req, F_CIB_ISREPLY) == NULL) { + crm_info("Shutdown REQ from %s", host); + return cib_ok; - } else if(cib_shutdown_flag) { - crm_info("Shutdown ACK from %s", host); - terminate_cib(__FUNCTION__); - return cib_ok; + } else if(cib_shutdown_flag) { + crm_info("Shutdown ACK from %s", host); + terminate_cib(__FUNCTION__); + return cib_ok; - } else { - crm_err("Shutdown ACK from %s - not shutting down",host); - result = cib_unknown; - } + } else { + crm_err("Shutdown ACK from %s - not shutting down",host); + result = cib_unknown; + } - return result; + return result; #endif } enum cib_errors cib_process_default( - const char *op, int options, const char *section, xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - enum cib_errors result = cib_ok; - crm_debug_2("Processing \"%s\" event", op); - *answer = NULL; + enum cib_errors result = cib_ok; + crm_debug_2("Processing \"%s\" event", op); + *answer = NULL; - if(op == NULL) { - result = cib_operation; - crm_err("No operation specified"); + if(op == NULL) { + result = cib_operation; + crm_err("No operation specified"); - } else if(strcasecmp(CRM_OP_NOOP, op) == 0) { - ; + } else if(strcasecmp(CRM_OP_NOOP, op) == 0) { + ; - } else { - result = cib_NOTSUPPORTED; - crm_err("Action [%s] is not supported by the CIB", op); - } - return result; + } else { + result = cib_NOTSUPPORTED; + crm_err("Action [%s] is not supported by the CIB", op); + } + return result; } enum cib_errors cib_process_quit( - const char *op, int options, const char *section, xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - enum cib_errors result = cib_ok; - crm_debug_2("Processing \"%s\" event", op); + enum cib_errors result = cib_ok; + crm_debug_2("Processing \"%s\" event", op); - crm_warn("The CRMd has asked us to exit... complying"); - exit(0); - return result; + crm_warn("The CRMd has asked us to exit... complying"); + exit(0); + return result; } enum cib_errors cib_process_readwrite( - const char *op, int options, const char *section, xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { #ifdef CIBPIPE return cib_invalid_argument; #else - enum cib_errors result = cib_ok; - crm_debug_2("Processing \"%s\" event", op); - - if(safe_str_eq(op, CIB_OP_ISMASTER)) { - if(cib_is_master == TRUE) { - result = cib_ok; - } else { - result = cib_not_master; - } - return result; + enum cib_errors result = cib_ok; + crm_debug_2("Processing \"%s\" event", op); + + if(safe_str_eq(op, CIB_OP_ISMASTER)) { + if(cib_is_master == TRUE) { + result = cib_ok; + } else { + result = cib_not_master; } + return result; + } - if(safe_str_eq(op, CIB_OP_MASTER)) { - if(cib_is_master == FALSE) { - crm_info("We are now in R/W mode"); - cib_is_master = TRUE; - syncd_once = TRUE; + if(safe_str_eq(op, CIB_OP_MASTER)) { + if(cib_is_master == FALSE) { + crm_info("We are now in R/W mode"); + cib_is_master = TRUE; + syncd_once = TRUE; - } else { - crm_debug("We are still in R/W mode"); - } - - } else if(cib_is_master) { - crm_info("We are now in R/O mode"); - cib_is_master = FALSE; + } else { + crm_debug("We are still in R/W mode"); } + + } else if(cib_is_master) { + crm_info("We are now in R/O mode"); + cib_is_master = FALSE; + } - return result; + return result; #endif } enum cib_errors cib_process_ping( - const char *op, int options, const char *section, xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { #ifdef CIBPIPE return cib_invalid_argument; #else - enum cib_errors result = cib_ok; - crm_debug_2("Processing \"%s\" event", op); - *answer = createPingAnswerFragment(CRM_SYSTEM_CIB, "ok"); - return result; + enum cib_errors result = cib_ok; + crm_debug_2("Processing \"%s\" event", op); + *answer = createPingAnswerFragment(CRM_SYSTEM_CIB, "ok"); + return result; #endif } enum cib_errors cib_process_sync( - const char *op, int options, const char *section, xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { #ifdef CIBPIPE return cib_invalid_argument; #else return sync_our_cib(req, TRUE); #endif } enum cib_errors cib_process_sync_one( - const char *op, int options, const char *section, xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { #ifdef CIBPIPE return cib_invalid_argument; #else return sync_our_cib(req, FALSE); #endif } int sync_in_progress = 0; enum cib_errors cib_server_process_diff( - const char *op, int options, const char *section, xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - int rc = cib_ok; + int rc = cib_ok; - if(cib_is_master) { - /* the master is never waiting for a resync */ - sync_in_progress = 0; - } + if(cib_is_master) { + /* the master is never waiting for a resync */ + sync_in_progress = 0; + } - if(sync_in_progress > MAX_DIFF_RETRY) { - /* request another full-sync, - * the last request may have been lost - */ - sync_in_progress = 0; - } - - if(sync_in_progress) { - int diff_add_updates = 0; - int diff_add_epoch = 0; - int diff_add_admin_epoch = 0; + if(sync_in_progress > MAX_DIFF_RETRY) { + /* request another full-sync, + * the last request may have been lost + */ + sync_in_progress = 0; + } + + if(sync_in_progress) { + int diff_add_updates = 0; + int diff_add_epoch = 0; + int diff_add_admin_epoch = 0; - int diff_del_updates = 0; - int diff_del_epoch = 0; - int diff_del_admin_epoch = 0; + int diff_del_updates = 0; + int diff_del_epoch = 0; + int diff_del_admin_epoch = 0; - cib_diff_version_details( - input, - &diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates, - &diff_del_admin_epoch, &diff_del_epoch, &diff_del_updates); + cib_diff_version_details( + input, + &diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates, + &diff_del_admin_epoch, &diff_del_epoch, &diff_del_updates); - sync_in_progress++; - crm_notice("Not applying diff %d.%d.%d -> %d.%d.%d (sync in progress)", - diff_del_admin_epoch,diff_del_epoch,diff_del_updates, - diff_add_admin_epoch,diff_add_epoch,diff_add_updates); - return cib_diff_resync; - } + sync_in_progress++; + crm_notice("Not applying diff %d.%d.%d -> %d.%d.%d (sync in progress)", + diff_del_admin_epoch,diff_del_epoch,diff_del_updates, + diff_add_admin_epoch,diff_add_epoch,diff_add_updates); + return cib_diff_resync; + } - rc = cib_process_diff(op, options, section, req, input, existing_cib, result_cib, answer); + rc = cib_process_diff(op, options, section, req, input, existing_cib, result_cib, answer); - if(rc == cib_diff_resync && cib_is_master == FALSE) { - xmlNode *sync_me = create_xml_node(NULL, "sync-me"); - free_xml(*result_cib); - *result_cib = NULL; - crm_info("Requesting re-sync from peer"); - sync_in_progress++; + if(rc == cib_diff_resync && cib_is_master == FALSE) { + xmlNode *sync_me = create_xml_node(NULL, "sync-me"); + free_xml(*result_cib); + *result_cib = NULL; + crm_info("Requesting re-sync from peer"); + sync_in_progress++; - crm_xml_add(sync_me, F_TYPE, "cib"); - crm_xml_add(sync_me, F_CIB_OPERATION, CIB_OP_SYNC_ONE); - crm_xml_add(sync_me, F_CIB_DELEGATED, cib_our_uname); - - if(send_cluster_message(NULL, crm_msg_cib, sync_me, FALSE) == FALSE) { - rc = cib_not_connected; - } - free_xml(sync_me); + crm_xml_add(sync_me, F_TYPE, "cib"); + crm_xml_add(sync_me, F_CIB_OPERATION, CIB_OP_SYNC_ONE); + crm_xml_add(sync_me, F_CIB_DELEGATED, cib_our_uname); + + if(send_cluster_message(NULL, crm_msg_cib, sync_me, FALSE) == FALSE) { + rc = cib_not_connected; + } + free_xml(sync_me); - } else if(rc == cib_diff_resync) { - rc = cib_diff_failed; - if(options & cib_force_diff) { - crm_warn("Not requesting full refresh in R/W mode"); - } + } else if(rc == cib_diff_resync) { + rc = cib_diff_failed; + if(options & cib_force_diff) { + crm_warn("Not requesting full refresh in R/W mode"); } + } - return rc; + return rc; } enum cib_errors cib_process_replace_svr( - const char *op, int options, const char *section, xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { const char *tag = crm_element_name(input); enum cib_errors rc = cib_process_replace( op, options, section, req, input, existing_cib, result_cib, answer); if(rc == cib_ok && safe_str_eq(tag, XML_TAG_CIB)) { sync_in_progress = 0; } return rc; } static int delete_cib_object(xmlNode *parent, xmlNode *delete_spec) { - const char *object_name = NULL; - const char *object_id = NULL; - xmlNode *equiv_node = NULL; - int result = cib_ok; + const char *object_name = NULL; + const char *object_id = NULL; + xmlNode *equiv_node = NULL; + int result = cib_ok; - if(delete_spec != NULL) { - object_name = crm_element_name(delete_spec); - } - object_id = crm_element_value(delete_spec, XML_ATTR_ID); + if(delete_spec != NULL) { + object_name = crm_element_name(delete_spec); + } + object_id = crm_element_value(delete_spec, XML_ATTR_ID); - crm_debug_3("Processing: <%s id=%s>", - crm_str(object_name), crm_str(object_id)); + crm_debug_3("Processing: <%s id=%s>", + crm_str(object_name), crm_str(object_id)); - if(delete_spec == NULL) { - result = cib_NOOBJECT; + if(delete_spec == NULL) { + result = cib_NOOBJECT; - } else if(parent == NULL) { - result = cib_NOPARENT; + } else if(parent == NULL) { + result = cib_NOPARENT; - } else if(object_id == NULL) { - /* placeholder object */ - equiv_node = find_xml_node(parent, object_name, FALSE); + } else if(object_id == NULL) { + /* placeholder object */ + equiv_node = find_xml_node(parent, object_name, FALSE); - } else { - equiv_node = find_entity(parent, object_name, object_id); - } + } else { + equiv_node = find_entity(parent, object_name, object_id); + } - if(result != cib_ok) { - ; /* nothing */ + if(result != cib_ok) { + ; /* nothing */ - } else if(equiv_node == NULL) { - result = cib_ok; - - } else if(xml_has_children(delete_spec) == FALSE) { - /* only leaves are deleted */ - crm_debug("Removing leaf: <%s id=%s>", - crm_str(object_name), crm_str(object_id)); - zap_xml_from_parent(parent, equiv_node); + } else if(equiv_node == NULL) { + result = cib_ok; - } else { - - xml_child_iter( - delete_spec, child, + } else if(xml_has_children(delete_spec) == FALSE) { + /* only leaves are deleted */ + crm_debug("Removing leaf: <%s id=%s>", + crm_str(object_name), crm_str(object_id)); + zap_xml_from_parent(parent, equiv_node); - int tmp_result = delete_cib_object(equiv_node, child); - - /* only the first error is likely to be interesting */ - if(tmp_result != cib_ok && result == cib_ok) { - result = tmp_result; - } - ); + } else { + xmlNode *child = NULL; + for(child = delete_spec; child != NULL; child = child->next) { + int tmp_result = delete_cib_object(equiv_node, child); + /* only the first error is likely to be interesting */ + if(tmp_result != cib_ok && result == cib_ok) { + result = tmp_result; + } } + } - return result; + return result; } enum cib_errors cib_process_delete_absolute( const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { xmlNode *failed = NULL; enum cib_errors result = cib_ok; xmlNode *update_section = NULL; crm_debug_2("Processing \"%s\" event for section=%s", op, crm_str(section)); if(safe_str_eq(XML_CIB_TAG_SECTION_ALL, section)) { section = NULL; } else if(safe_str_eq(XML_TAG_CIB, section)) { section = NULL; } else if(safe_str_eq(crm_element_name(input), XML_TAG_CIB)) { section = NULL; } CRM_CHECK(strcasecmp(CIB_OP_DELETE, op) == 0, return cib_operation); if(input == NULL) { crm_err("Cannot perform modification with no data"); return cib_NOOBJECT; } failed = create_xml_node(NULL, XML_TAG_FAILED); update_section = get_object_root(section, *result_cib); result = delete_cib_object(update_section, input); update_results(failed, input, op, result); if(xml_has_children(failed)) { CRM_CHECK(result != cib_ok, result = cib_unknown); } if (result != cib_ok) { crm_log_xml_err(failed, "CIB Update failures"); *answer = failed; } else { free_xml(failed); } return result; } gboolean check_generation(xmlNode *newCib, xmlNode *oldCib) { - if(cib_compare_generation(newCib, oldCib) >= 0) { - return TRUE; - } + if(cib_compare_generation(newCib, oldCib) >= 0) { + return TRUE; + } - crm_warn("Generation from update is older than the existing one"); - return FALSE; + crm_warn("Generation from update is older than the existing one"); + return FALSE; } #ifndef CIBPIPE enum cib_errors sync_our_cib(xmlNode *request, gboolean all) { - enum cib_errors result = cib_ok; - const char *host = crm_element_value(request, F_ORIG); - const char *op = crm_element_value(request, F_CIB_OPERATION); + enum cib_errors result = cib_ok; + const char *host = crm_element_value(request, F_ORIG); + const char *op = crm_element_value(request, F_CIB_OPERATION); - xmlNode *replace_request = cib_msg_copy(request, FALSE); + xmlNode *replace_request = cib_msg_copy(request, FALSE); - CRM_CHECK(the_cib != NULL, ;); - CRM_CHECK(replace_request != NULL, ;); + CRM_CHECK(the_cib != NULL, ;); + CRM_CHECK(replace_request != NULL, ;); - crm_debug("Syncing CIB to %s", all?"all peers":host); - if(all == FALSE && host == NULL) { - crm_log_xml(LOG_ERR, "bad sync", request); - } + crm_debug("Syncing CIB to %s", all?"all peers":host); + if(all == FALSE && host == NULL) { + crm_log_xml(LOG_ERR, "bad sync", request); + } - /* remove the "all == FALSE" condition - * - * sync_from was failing, the local client wasnt being notified - * because it didnt know it was a reply - * setting this does not prevent the other nodes from applying it - * if all == TRUE - */ - if(host != NULL) { - crm_xml_add(replace_request, F_CIB_ISREPLY, host); - } - crm_xml_add(replace_request, F_CIB_OPERATION, CIB_OP_REPLACE); - crm_xml_add(replace_request, "original_"F_CIB_OPERATION, op); - crm_xml_add(replace_request, F_CIB_GLOBAL_UPDATE, XML_BOOLEAN_TRUE); - add_message_xml(replace_request, F_CIB_CALLDATA, the_cib); + /* remove the "all == FALSE" condition + * + * sync_from was failing, the local client wasnt being notified + * because it didnt know it was a reply + * setting this does not prevent the other nodes from applying it + * if all == TRUE + */ + if(host != NULL) { + crm_xml_add(replace_request, F_CIB_ISREPLY, host); + } + crm_xml_add(replace_request, F_CIB_OPERATION, CIB_OP_REPLACE); + crm_xml_add(replace_request, "original_"F_CIB_OPERATION, op); + crm_xml_add(replace_request, F_CIB_GLOBAL_UPDATE, XML_BOOLEAN_TRUE); + add_message_xml(replace_request, F_CIB_CALLDATA, the_cib); - if(send_cluster_message(all?NULL:host, crm_msg_cib, replace_request, FALSE) == FALSE) { - result = cib_not_connected; - } - free_xml(replace_request); - return result; + if(send_cluster_message(all?NULL:host, crm_msg_cib, replace_request, FALSE) == FALSE) { + result = cib_not_connected; + } + free_xml(replace_request); + return result; } #endif diff --git a/crmd/lrm.c b/crmd/lrm.c index 83da735e21..cc2f49fd03 100644 --- a/crmd/lrm.c +++ b/crmd/lrm.c @@ -1,1894 +1,1898 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define START_DELAY_THRESHOLD 5 * 60 * 1000 struct recurring_op_s { char *rsc_id; char *op_key; int call_id; int interval; gboolean remove; gboolean cancelled; }; struct pending_deletion_op_s { char *rsc; ha_msg_input_t *input; }; struct delete_event_s { int rc; const char *rsc; }; char *make_stop_id(const char *rsc, int call_id); void cib_rsc_callback(xmlNode *msg, int call_id, int rc, xmlNode *output, void *user_data); gboolean build_operation_update( xmlNode *rsc_list, lrm_rsc_t *rsc, lrm_op_t *op, const char *src, int lpc, int level); gboolean build_active_RAs(xmlNode *rsc_list); gboolean is_rsc_active(const char *rsc_id); int do_update_resource(lrm_op_t *op); gboolean process_lrm_event(lrm_op_t *op); void do_lrm_rsc_op(lrm_rsc_t *rsc, const char *operation, xmlNode *msg, xmlNode *request); lrm_op_t *construct_op( xmlNode *rsc_op, const char *rsc_id, const char *operation); void send_direct_ack(const char *to_host, const char *to_sys, lrm_rsc_t *rsc, lrm_op_t* op, const char *rsc_id); void free_recurring_op(gpointer value); void free_deletion_op(gpointer value); GHashTable *resources = NULL; GHashTable *pending_ops = NULL; GHashTable *deletion_ops = NULL; GCHSource *lrm_source = NULL; int num_lrm_register_fails = 0; int max_lrm_register_fails = 30; void lrm_connection_destroy(gpointer user_data) { if(is_set(fsa_input_register, R_LRM_CONNECTED)) { crm_crit("LRM Connection failed"); register_fsa_input(C_FSA_INTERNAL, I_ERROR, NULL); clear_bit_inplace(fsa_input_register, R_LRM_CONNECTED); } else { crm_info("LRM Connection disconnected"); } lrm_source = NULL; } /* A_LRM_CONNECT */ void do_lrm_control(long long action, enum crmd_fsa_cause cause, enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input, fsa_data_t *msg_data) { if(fsa_lrm_conn == NULL) { register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL); return; } if(action & A_LRM_DISCONNECT) { if(verify_stopped(cur_state, LOG_INFO) == FALSE) { crmd_fsa_stall(NULL); return; } if(is_set(fsa_input_register, R_LRM_CONNECTED)) { clear_bit_inplace(fsa_input_register, R_LRM_CONNECTED); fsa_lrm_conn->lrm_ops->signoff(fsa_lrm_conn); crm_info("Disconnected from the LRM"); } /* TODO: Clean up the hashtable */ } if(action & A_LRM_CONNECT) { int ret = HA_OK; deletion_ops = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, free_deletion_op); pending_ops = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, free_recurring_op); resources = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); if(ret == HA_OK) { crm_debug("Connecting to the LRM"); ret = fsa_lrm_conn->lrm_ops->signon( fsa_lrm_conn, CRM_SYSTEM_CRMD); } if(ret != HA_OK) { if(++num_lrm_register_fails < max_lrm_register_fails) { crm_warn("Failed to sign on to the LRM %d" " (%d max) times", num_lrm_register_fails, max_lrm_register_fails); crm_timer_start(wait_timer); crmd_fsa_stall(NULL); return; } } if(ret == HA_OK) { crm_debug_4("LRM: set_lrm_callback..."); ret = fsa_lrm_conn->lrm_ops->set_lrm_callback( fsa_lrm_conn, lrm_op_callback); if(ret != HA_OK) { crm_err("Failed to set LRM callbacks"); } } if(ret != HA_OK) { crm_err("Failed to sign on to the LRM %d" " (max) times", num_lrm_register_fails); register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL); return; } /* TODO: create a destroy handler that causes * some recovery to happen */ lrm_source = G_main_add_IPC_Channel( G_PRIORITY_LOW, fsa_lrm_conn->lrm_ops->ipcchan(fsa_lrm_conn), FALSE, lrm_dispatch, fsa_lrm_conn, lrm_connection_destroy); set_bit_inplace(fsa_input_register, R_LRM_CONNECTED); crm_debug("LRM connection established"); } if(action & ~(A_LRM_CONNECT|A_LRM_DISCONNECT)) { crm_err("Unexpected action %s in %s", fsa_action2string(action), __FUNCTION__); } } static void ghash_print_pending(gpointer key, gpointer value, gpointer user_data) { const char *stop_id = key; int *log_level = user_data; struct recurring_op_s *pending = value; do_crm_log(*log_level, "Pending action: %s (%s)", stop_id, pending->op_key); } static void ghash_print_pending_for_rsc(gpointer key, gpointer value, gpointer user_data) { const char *stop_id = key; char *rsc = user_data; struct recurring_op_s *pending = value; if(safe_str_eq(rsc, pending->rsc_id)) { do_crm_log(LOG_NOTICE, "%sction %s (%s) incomplete at shutdown", pending->interval==0?"A":"Recurring a", stop_id, pending->op_key); } } static void ghash_count_pending(gpointer key, gpointer value, gpointer user_data) { int *counter = user_data; struct recurring_op_s *pending = value; if(pending->interval > 0) { /* Ignore recurring actions in the shutdown calculations */ return; } (*counter)++; } gboolean verify_stopped(enum crmd_fsa_state cur_state, int log_level) { int counter = 0; GListPtr lpc = NULL; gboolean rc = TRUE; GListPtr lrm_list = NULL; crm_debug("Checking for active resources before exit"); if(cur_state == S_TERMINATE) { log_level = LOG_ERR; } if(pending_ops) { g_hash_table_foreach(pending_ops, ghash_count_pending, &counter); } if(counter > 0) { rc = FALSE; do_crm_log(log_level, "%d pending LRM operations at shutdown%s", counter, cur_state == S_TERMINATE?"":"... waiting"); if(cur_state == S_TERMINATE || !is_set(fsa_input_register, R_SENT_RSC_STOP)) { g_hash_table_foreach( pending_ops, ghash_print_pending, &log_level); } goto bail; } if(is_set(fsa_input_register, R_LRM_CONNECTED)) { lrm_list = fsa_lrm_conn->lrm_ops->get_all_rscs(fsa_lrm_conn); } for(lpc = lrm_list; lpc != NULL; lpc = lpc->next) { char *rsc_id = (char*)lpc->data; if(is_rsc_active(rsc_id) == FALSE) { continue; } crm_err("Resource %s was active at shutdown." " You may ignore this error if it is unmanaged.", rsc_id); g_hash_table_foreach( pending_ops, ghash_print_pending_for_rsc, rsc_id); } - slist_destroy(char, rid, lrm_list, free(rid)); + slist_basic_destroy(lrm_list); bail: set_bit_inplace(fsa_input_register, R_SENT_RSC_STOP); if(cur_state == S_TERMINATE) { rc = TRUE; } return rc; } static char * get_rsc_metadata(const char *type, const char *class, const char *provider) { char *metadata = NULL; CRM_CHECK(type != NULL, return NULL); CRM_CHECK(class != NULL, return NULL); if(provider == NULL) { provider = "heartbeat"; } crm_debug_2("Retreiving metadata for %s::%s:%s", type, class, provider); metadata = fsa_lrm_conn->lrm_ops->get_rsc_type_metadata( fsa_lrm_conn, class, type, provider); if(metadata) { /* copy the metadata because the LRM likes using * g_alloc instead of cl_malloc */ char *m_copy = crm_strdup(metadata); g_free(metadata); metadata = m_copy; } else { crm_warn("No metadata found for %s::%s:%s", type, class, provider); } return metadata; } typedef struct reload_data_s { char *key; char *metadata; time_t last_query; gboolean can_reload; GListPtr restart_list; } reload_data_t; static void g_hash_destroy_reload(gpointer data) { reload_data_t *reload = data; crm_free(reload->key); crm_free(reload->metadata); - slist_destroy(char, child, reload->restart_list, crm_free(child)); + slist_basic_destroy(reload->restart_list); crm_free(reload); } GHashTable *reload_hash = NULL; static GListPtr get_rsc_restart_list(lrm_rsc_t *rsc, lrm_op_t *op) { int len = 0; char *key = NULL; char *copy = NULL; const char *value = NULL; const char *provider = NULL; + xmlNode *param = NULL; xmlNode *params = NULL; xmlNode *actions = NULL; xmlNode *metadata = NULL; time_t now = time(NULL); reload_data_t *reload = NULL; if(reload_hash == NULL) { reload_hash = g_hash_table_new_full( g_str_hash, g_str_equal, NULL, g_hash_destroy_reload); } provider = rsc->provider; if(provider == NULL) { provider = "heartbeat"; } len = strlen(rsc->type) + strlen(rsc->class) + strlen(provider) + 4; crm_malloc(key, len); snprintf(key, len, "%s::%s:%s", rsc->type, rsc->class, provider); reload = g_hash_table_lookup(reload_hash, key); if(reload && ((now - 9) > reload->last_query) && safe_str_eq(op->op_type, RSC_START)) { reload = NULL; /* re-query */ } if(reload == NULL) { + xmlNode *action = NULL; crm_malloc0(reload, sizeof(reload_data_t)); g_hash_table_replace(reload_hash, key, reload); reload->last_query = now; reload->key = key; key = NULL; reload->metadata = get_rsc_metadata(rsc->type, rsc->class, provider); metadata = string2xml(reload->metadata); if(metadata == NULL) { crm_err("Metadata for %s::%s:%s is not valid XML", rsc->provider, rsc->class, rsc->type); goto cleanup; } actions = find_xml_node(metadata, "actions", TRUE); - xml_child_iter_filter( - actions, action, "action", - value = crm_element_value(action, "name"); - if(safe_str_eq("reload", value)) { - reload->can_reload = TRUE; - break; + for(action = actions; action != NULL; action = action->next) { + if(crm_str_eq((const char *)action->name, "action", TRUE)) { + value = crm_element_value(action, "name"); + if(safe_str_eq("reload", value)) { + reload->can_reload = TRUE; + break; + } } - ); + } if(reload->can_reload == FALSE) { goto cleanup; } params = find_xml_node(metadata, "parameters", TRUE); - xml_child_iter_filter( - params, param, "parameter", - value = crm_element_value(param, "unique"); - if(crm_is_true(value)) { - value = crm_element_value(param, "name"); - if(value == NULL) { - crm_err("%s: NULL param", key); - continue; + for(param = params; param != NULL; param = param->next) { + if(crm_str_eq((const char *)param->name, "parameter", TRUE)) { + value = crm_element_value(param, "unique"); + if(crm_is_true(value)) { + value = crm_element_value(param, "name"); + if(value == NULL) { + crm_err("%s: NULL param", key); + continue; + } + crm_debug("Attr %s is not reloadable", value); + copy = crm_strdup(value); + CRM_CHECK(copy != NULL, continue); + reload->restart_list = g_list_append(reload->restart_list, copy); } - crm_debug("Attr %s is not reloadable", value); - copy = crm_strdup(value); - CRM_CHECK(copy != NULL, continue); - reload->restart_list = g_list_append(reload->restart_list, copy); } - ); + } } cleanup: crm_free(key); free_xml(metadata); return reload?reload->restart_list:NULL; } static void append_restart_list(lrm_rsc_t *rsc, lrm_op_t *op, xmlNode *update, const char *version) { int len = 0; char *list = NULL; char *digest = NULL; const char *value = NULL; gboolean non_empty = FALSE; xmlNode *restart = NULL; GListPtr restart_list = NULL; GListPtr lpc = NULL; if(op->interval > 0) { /* monitors are not reloadable */ return; } else if(op->params == NULL) { crm_debug("%s has no parameters", ID(update)); return; } else if(rsc == NULL) { return; } else if(crm_str_eq(CRMD_ACTION_START, op->op_type, TRUE) == FALSE) { /* only starts are potentially reloadable */ return; } else if(compare_version("1.0.8", version) > 0) { /* Caller version does not support reloads */ return; } restart_list = get_rsc_restart_list(rsc, op); if(restart_list == NULL) { /* Resource does not support reloads */ return; } restart = create_xml_node(NULL, XML_TAG_PARAMS); for(lpc = restart_list; lpc != NULL; lpc = lpc->next) { const char *param = (const char*)lpc->data; int start = len; CRM_CHECK(param != NULL, continue); value = g_hash_table_lookup(op->params, param); if(value != NULL) { non_empty = TRUE; crm_xml_add(restart, param, value); } len += strlen(param) + 2; crm_realloc(list, len+1); sprintf(list+start, " %s ", param); } digest = calculate_operation_digest(restart, version); crm_xml_add(update, XML_LRM_ATTR_OP_RESTART, list); crm_xml_add(update, XML_LRM_ATTR_RESTART_DIGEST, digest); #if 0 crm_debug("%s: %s, %s", rsc->id, digest, list); if(non_empty) { crm_log_xml_debug(restart, "restart digest source"); } #endif free_xml(restart); crm_free(digest); crm_free(list); } gboolean build_operation_update( xmlNode *parent, lrm_rsc_t *rsc, lrm_op_t *op, const char *src, int lpc, int level) { xmlNode *xml_op = NULL; const char *caller_version = CRM_FEATURE_SET; if(AM_I_DC) { } else if(fsa_our_dc_version != NULL) { caller_version = fsa_our_dc_version; } else if(op->params == NULL) { caller_version = fsa_our_dc_version; } else { /* there is a small risk in formerly mixed clusters that * it will be sub-optimal. * however with our upgrade policy, the update we send * should still be completely supported anyway */ caller_version = g_hash_table_lookup( op->params, XML_ATTR_CRM_VERSION); crm_warn("Falling back to operation originator version: %s", caller_version); } xml_op = create_operation_update(parent, op, caller_version, 0, src, level); if(xml_op) { append_restart_list(rsc, op, xml_op, caller_version); } return TRUE; } gboolean is_rsc_active(const char *rsc_id) { GListPtr llpc = NULL; GList *op_list = NULL; gboolean active = FALSE; lrm_rsc_t *the_rsc = NULL; state_flag_t cur_state = 0; int max_call_id = -1; if(fsa_lrm_conn == NULL) { return FALSE; } the_rsc = fsa_lrm_conn->lrm_ops->get_rsc(fsa_lrm_conn, rsc_id); crm_debug_3("Processing lrm_rsc_t entry %s", rsc_id); if(the_rsc == NULL) { crm_err("NULL resource returned from the LRM"); return FALSE; } op_list = the_rsc->ops->get_cur_state(the_rsc, &cur_state); crm_debug_3("\tcurrent state:%s",cur_state==LRM_RSC_IDLE?"Idle":"Busy"); for(llpc = op_list; llpc != NULL; llpc = llpc->next) { lrm_op_t *op = (lrm_op_t*)llpc->data; crm_debug_2("Processing op %s_%d (%d) for %s (status=%d, rc=%d)", op->op_type, op->interval, op->call_id, the_rsc->id, op->op_status, op->rc); CRM_ASSERT(max_call_id <= op->call_id); if(op->rc == EXECRA_OK && safe_str_eq(op->op_type, CRMD_ACTION_STOP)) { active = FALSE; } else if(op->rc == EXECRA_OK && safe_str_eq(op->op_type, CRMD_ACTION_MIGRATE)) { /* a stricter check is too complex... * leave that to the PE */ active = FALSE; } else if(op->rc == EXECRA_NOT_RUNNING) { active = FALSE; } else { active = TRUE; } max_call_id = op->call_id; lrm_free_op(op); } g_list_free(op_list); lrm_free_rsc(the_rsc); return active; } gboolean build_active_RAs(xmlNode *rsc_list) { GListPtr lpc = NULL; GListPtr llpc = NULL; GList *op_list = NULL; GList *lrm_list = NULL; gboolean found_op = FALSE; state_flag_t cur_state = 0; if(fsa_lrm_conn == NULL) { return FALSE; } lrm_list = fsa_lrm_conn->lrm_ops->get_all_rscs(fsa_lrm_conn); for(lpc = lrm_list; lpc != NULL; lpc = lpc->next) { char *rid = (char*)lpc->data; int max_call_id = -1; xmlNode *xml_rsc = NULL; lrm_rsc_t *the_rsc = fsa_lrm_conn->lrm_ops->get_rsc(fsa_lrm_conn, rid); if(the_rsc == NULL) { crm_err("NULL resource returned from the LRM: %s", rid); continue; } xml_rsc = create_xml_node(rsc_list, XML_LRM_TAG_RESOURCE); crm_xml_add(xml_rsc, XML_ATTR_ID, the_rsc->id); crm_xml_add(xml_rsc, XML_ATTR_TYPE, the_rsc->type); crm_xml_add(xml_rsc, XML_AGENT_ATTR_CLASS, the_rsc->class); crm_xml_add(xml_rsc, XML_AGENT_ATTR_PROVIDER,the_rsc->provider); op_list = the_rsc->ops->get_cur_state(the_rsc, &cur_state); for(llpc = op_list; llpc != NULL; llpc = llpc->next) { lrm_op_t *op = (lrm_op_t*)llpc->data; if(max_call_id < op->call_id) { build_operation_update( xml_rsc, the_rsc, op, __FUNCTION__, 0, LOG_DEBUG); } else if(max_call_id > op->call_id) { crm_err("Bad call_id in list=%d. Previous call_id=%d", op->call_id, max_call_id); } else { crm_warn("lrm->get_cur_state() returned" " duplicate entries for call_id=%d", op->call_id); } max_call_id = op->call_id; found_op = TRUE; lrm_free_op(op); } if(found_op == FALSE && g_list_length(op_list) != 0) { crm_err("Could not properly determin last op" " for %s from %d entries", the_rsc->id, g_list_length(op_list)); } g_list_free(op_list); lrm_free_rsc(the_rsc); } - slist_destroy(char, rid, lrm_list, free(rid)); + slist_basic_destroy(lrm_list); return TRUE; } xmlNode* do_lrm_query(gboolean is_replace) { gboolean shut_down = FALSE; xmlNode *xml_result= NULL; xmlNode *xml_state = NULL; xmlNode *xml_data = NULL; xmlNode *rsc_list = NULL; const char *exp_state = CRMD_STATE_ACTIVE; if(is_set(fsa_input_register, R_SHUTDOWN)) { exp_state = CRMD_STATE_INACTIVE; shut_down = TRUE; } xml_state = create_node_state( fsa_our_uname, ACTIVESTATUS, XML_BOOLEAN_TRUE, ONLINESTATUS, CRMD_JOINSTATE_MEMBER, exp_state, !shut_down, __FUNCTION__); xml_data = create_xml_node(xml_state, XML_CIB_TAG_LRM); crm_xml_add(xml_data, XML_ATTR_ID, fsa_our_uuid); rsc_list = create_xml_node(xml_data, XML_LRM_TAG_RESOURCES); /* Build a list of active (not always running) resources */ build_active_RAs(rsc_list); xml_result = create_cib_fragment(xml_state, XML_CIB_TAG_STATUS); crm_log_xml_debug_3(xml_state, "Current state of the LRM"); free_xml(xml_state); return xml_result; } static void notify_deleted(ha_msg_input_t *input, const char *rsc_id, int rc) { lrm_op_t* op = NULL; const char *from_sys = crm_element_value(input->msg, F_CRM_SYS_FROM); const char *from_host = crm_element_value(input->msg, F_CRM_HOST_FROM); crm_info("Notifying %s on %s that %s was%s deleted", from_sys, from_host, rsc_id, rc==HA_OK?"":" not"); op = construct_op(input->xml, rsc_id, CRMD_ACTION_DELETE); CRM_ASSERT(op != NULL); if(rc == HA_OK) { op->op_status = LRM_OP_DONE; op->rc = EXECRA_OK; } else { op->op_status = LRM_OP_ERROR; op->rc = EXECRA_UNKNOWN_ERROR; } send_direct_ack(from_host, from_sys, NULL, op, rsc_id); free_lrm_op(op); if(safe_str_neq(from_sys, CRM_SYSTEM_TENGINE)) { /* this isn't expected - trigger a new transition */ time_t now = time(NULL); char *now_s = crm_itoa(now); crm_debug("Triggering a refresh after %s deleted %s from the LRM", from_sys, rsc_id); update_attr(fsa_cib_conn, cib_none, XML_CIB_TAG_CRMCONFIG, NULL, NULL, NULL, NULL, "last-lrm-refresh", now_s, FALSE); crm_free(now_s); } } static gboolean lrm_remove_deleted_rsc( gpointer key, gpointer value, gpointer user_data) { struct delete_event_s *event = user_data; struct pending_deletion_op_s *op = value; if(safe_str_eq(event->rsc, op->rsc)) { notify_deleted(op->input, event->rsc, event->rc); return TRUE; } return FALSE; } static gboolean lrm_remove_deleted_op( gpointer key, gpointer value, gpointer user_data) { const char *rsc = user_data; struct recurring_op_s *pending = value; if(safe_str_eq(rsc, pending->rsc_id)) { crm_info("Removing op %s:%d for deleted resource %s", pending->op_key, pending->call_id, rsc); return TRUE; } return FALSE; } /* * Remove the rsc from the CIB * * Avoids refreshing the entire LRM section of this host */ #define rsc_template "//"XML_CIB_TAG_STATE"[@uname='%s']//"XML_LRM_TAG_RESOURCE"[@id='%s']" static void delete_rsc_entry(ha_msg_input_t *input, const char *rsc_id, int rc) { struct delete_event_s event; CRM_CHECK(rsc_id != NULL, return); if(rc == HA_OK) { char *rsc_xpath = NULL; char *rsc_id_copy = crm_strdup(rsc_id); int max = strlen(rsc_template) + strlen(rsc_id) + strlen(fsa_our_uname) + 1; crm_malloc0(rsc_xpath, max); snprintf(rsc_xpath, max, rsc_template, fsa_our_uname, rsc_id); CRM_CHECK(rsc_id != NULL, return); crm_debug("sync: Sending delete op for %s", rsc_id); fsa_cib_conn->cmds->delete( fsa_cib_conn, rsc_xpath, NULL, cib_quorum_override|cib_xpath); g_hash_table_foreach_remove(pending_ops, lrm_remove_deleted_op, rsc_id_copy); crm_free(rsc_id_copy); crm_free(rsc_xpath); } if(input) { notify_deleted(input, rsc_id, rc); } event.rc = rc; event.rsc = rsc_id; g_hash_table_foreach_remove(deletion_ops, lrm_remove_deleted_rsc, &event); } /* * Remove the op from the CIB * * Avoids refreshing the entire LRM section of this host */ #define op_template "//"XML_CIB_TAG_STATE"[@uname='%s']//"XML_LRM_TAG_RESOURCE"[@id='%s']/"XML_LRM_TAG_RSC_OP"[@id='%s']" #define op_call_template "//"XML_CIB_TAG_STATE"[@uname='%s']//"XML_LRM_TAG_RESOURCE"[@id='%s']/"XML_LRM_TAG_RSC_OP"[@id='%s' and @"XML_LRM_ATTR_CALLID"='%d']" static void delete_op_entry(lrm_op_t *op, const char *rsc_id, const char *key, int call_id) { xmlNode *xml_top = NULL; if(op != NULL) { xml_top = create_xml_node(NULL, XML_LRM_TAG_RSC_OP); crm_xml_add_int(xml_top, XML_LRM_ATTR_CALLID, op->call_id); crm_xml_add(xml_top, XML_ATTR_TRANSITION_KEY, op->user_data); crm_debug("async: Sending delete op for %s_%s_%d (call=%d)", op->rsc_id, op->op_type, op->interval, op->call_id); fsa_cib_conn->cmds->delete( fsa_cib_conn, XML_CIB_TAG_STATUS, xml_top, cib_quorum_override); } else if (rsc_id != NULL && key != NULL) { int max = 0; char *op_xpath = NULL; if(call_id > 0) { max = strlen(op_call_template) + strlen(rsc_id) + strlen(fsa_our_uname) + strlen(key) + 10; crm_malloc0(op_xpath, max); snprintf(op_xpath, max, op_call_template, fsa_our_uname, rsc_id, key, call_id); } else { max = strlen(op_template) + strlen(rsc_id) + strlen(fsa_our_uname) + strlen(key) + 1; crm_malloc0(op_xpath, max); snprintf(op_xpath, max, op_template, fsa_our_uname, rsc_id, key); } crm_debug("sync: Sending delete op for %s (call=%d)", rsc_id, call_id); fsa_cib_conn->cmds->delete( fsa_cib_conn, op_xpath, NULL, cib_quorum_override|cib_xpath); crm_free(op_xpath); } else { crm_err("Not enough information to delete op entry: rsc=%p key=%p", rsc_id, key); return; } crm_log_xml_debug_2(xml_top, "op:cancel"); free_xml(xml_top); } static gboolean cancel_op(lrm_rsc_t *rsc, const char *key, int op, gboolean remove) { int rc = HA_OK; struct recurring_op_s *pending = NULL; CRM_CHECK(op != 0, return FALSE); CRM_CHECK(rsc != NULL, return FALSE); if(key == NULL) { key = make_stop_id(rsc->id, op); } pending = g_hash_table_lookup(pending_ops, key); if(pending) { if(remove && pending->remove == FALSE) { pending->remove = TRUE; crm_debug("Scheduling %s for removal", key); } if(pending->cancelled) { crm_debug("Operation %s already cancelled", key); return TRUE; } pending->cancelled = TRUE; } else { crm_info("No pending op found for %s", key); } crm_debug("Cancelling op %d for %s (%s)", op, rsc->id, key); rc = rsc->ops->cancel_op(rsc, op); if(rc == HA_OK) { crm_debug("Op %d for %s (%s): cancelled", op, rsc->id, key); #ifdef HAVE_LRM_OP_T_RSC_DELETED } else if(rc == HA_RSCBUSY) { crm_debug("Op %d for %s (%s): cancelation pending", op, rsc->id, key); #endif } else { crm_debug("Op %d for %s (%s): Nothing to cancel", op, rsc->id, key); /* The caller needs to make sure the entry is * removed from the pending_ops list * * Usually by returning TRUE inside the worker function * supplied to g_hash_table_foreach_remove() * * Not removing the entry from pending_ops will block * the node from shutting down */ return FALSE; } return TRUE; } struct cancel_data { gboolean done; gboolean remove; const char *key; lrm_rsc_t *rsc; }; static gboolean cancel_action_by_key(gpointer key, gpointer value, gpointer user_data) { struct cancel_data *data = user_data; struct recurring_op_s *op = (struct recurring_op_s*)value; if(safe_str_eq(op->op_key, data->key)) { data->done = TRUE; if (cancel_op(data->rsc, key, op->call_id, data->remove) == FALSE) { return TRUE; } } return FALSE; } static gboolean cancel_op_key(lrm_rsc_t *rsc, const char *key, gboolean remove) { struct cancel_data data; CRM_CHECK(rsc != NULL, return FALSE); CRM_CHECK(key != NULL, return FALSE); data.key = key; data.rsc = rsc; data.done = FALSE; data.remove = remove; g_hash_table_foreach_remove(pending_ops, cancel_action_by_key, &data); return data.done; } static lrm_rsc_t * get_lrm_resource(xmlNode *resource, xmlNode *op_msg, gboolean do_create) { char rid[RID_LEN]; lrm_rsc_t *rsc = NULL; const char *short_id = ID(resource); const char *long_id = crm_element_value(resource, XML_ATTR_ID_LONG); crm_debug_2("Retrieving %s from the LRM.", short_id); CRM_CHECK(short_id != NULL, return NULL); if(rsc == NULL) { /* check if its already there (short name) */ strncpy(rid, short_id, RID_LEN); rid[RID_LEN-1] = 0; rsc = fsa_lrm_conn->lrm_ops->get_rsc(fsa_lrm_conn, rid); } if(rsc == NULL && long_id != NULL) { /* try the long name instead */ strncpy(rid, long_id, RID_LEN); rid[RID_LEN-1] = 0; rsc = fsa_lrm_conn->lrm_ops->get_rsc(fsa_lrm_conn, rid); } if(rsc == NULL && do_create) { /* add it to the LRM */ const char *type = crm_element_value(resource, XML_ATTR_TYPE); const char *class = crm_element_value(resource, XML_AGENT_ATTR_CLASS); const char *provider = crm_element_value(resource, XML_AGENT_ATTR_PROVIDER); GHashTable *params = xml2list(op_msg); CRM_CHECK(class != NULL, return NULL); CRM_CHECK(type != NULL, return NULL); crm_debug_2("Adding rsc %s before operation", short_id); strncpy(rid, short_id, RID_LEN); rid[RID_LEN-1] = 0; if(g_hash_table_size(params) == 0) { crm_log_xml_warn(op_msg, "EmptyParams"); } if(params != NULL) { g_hash_table_remove(params, CRM_META"_op_target_rc"); } fsa_lrm_conn->lrm_ops->add_rsc( fsa_lrm_conn, rid, class, type, provider, params); rsc = fsa_lrm_conn->lrm_ops->get_rsc(fsa_lrm_conn, rid); g_hash_table_destroy(params); if(rsc == NULL) { fsa_data_t *msg_data = NULL; crm_err("Could not add resource %s to LRM", rid); register_fsa_error(C_FSA_INTERNAL, I_FAIL, NULL); } } return rsc; } /* A_LRM_INVOKE */ void do_lrm_invoke(long long action, enum crmd_fsa_cause cause, enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input, fsa_data_t *msg_data) { gboolean done = FALSE; gboolean create_rsc = TRUE; const char *crm_op = NULL; const char *from_sys = NULL; const char *from_host = NULL; const char *operation = NULL; ha_msg_input_t *input = fsa_typed_data(fsa_dt_ha_msg); crm_op = crm_element_value(input->msg, F_CRM_TASK); from_sys = crm_element_value(input->msg, F_CRM_SYS_FROM); if(safe_str_neq(from_sys, CRM_SYSTEM_TENGINE)) { from_host = crm_element_value(input->msg, F_CRM_HOST_FROM); } crm_debug_2("LRM command from: %s", from_sys); if(safe_str_eq(crm_op, CRM_OP_LRM_DELETE)) { operation = CRMD_ACTION_DELETE; } else if(safe_str_eq(operation, CRM_OP_LRM_REFRESH)) { crm_op = CRM_OP_LRM_REFRESH; } else if(safe_str_eq(crm_op, CRM_OP_LRM_FAIL)) { #if HAVE_STRUCT_LRM_OPS_FAIL_RSC int rc = HA_OK; lrm_op_t* op = NULL; lrm_rsc_t *rsc = NULL; xmlNode *xml_rsc = find_xml_node( input->xml, XML_CIB_TAG_RESOURCE, TRUE); CRM_CHECK(xml_rsc != NULL, return); op = construct_op(input->xml, ID(xml_rsc), "fail"); op->op_status = LRM_OP_ERROR; op->rc = EXECRA_UNKNOWN_ERROR; CRM_ASSERT(op != NULL); rsc = get_lrm_resource(xml_rsc, input->xml, create_rsc); if(rsc) { crm_info("Failing resource %s...", rsc->id); rc = fsa_lrm_conn->lrm_ops->fail_rsc(fsa_lrm_conn, rsc->id, 1, "do_lrm_invoke: Async failure"); if(rc != HA_OK) { crm_err("Could not initiate an asynchronous failure for %s (%d)", rsc->id, rc); } else { op->op_status = LRM_OP_DONE; op->rc = EXECRA_OK; } lrm_free_rsc(rsc); } else { crm_info("Cannot find/create resource in order to fail it..."); crm_log_xml_warn(input->msg, "bad input"); } send_direct_ack(from_host, from_sys, NULL, op, ID(xml_rsc)); free_lrm_op(op); return; #else crm_info("Failing resource..."); operation = "fail"; #endif } else if(input->xml != NULL) { operation = crm_element_value(input->xml, XML_LRM_ATTR_TASK); } if(safe_str_eq(crm_op, CRM_OP_LRM_REFRESH)) { enum cib_errors rc = cib_ok; xmlNode *fragment = do_lrm_query(TRUE); crm_info("Forcing a local LRM refresh"); fsa_cib_update(XML_CIB_TAG_STATUS, fragment, cib_quorum_override, rc); free_xml(fragment); } else if(safe_str_eq(crm_op, CRM_OP_LRM_QUERY)) { xmlNode *data = do_lrm_query(FALSE); xmlNode *reply = create_reply(input->msg, data); if(relay_message(reply, TRUE) == FALSE) { crm_err("Unable to route reply"); crm_log_xml(LOG_ERR, "reply", reply); } free_xml(reply); free_xml(data); } else if(safe_str_eq(operation, CRM_OP_PROBED) || safe_str_eq(crm_op, CRM_OP_REPROBE)) { int cib_options = cib_inhibit_notify; const char *probed = XML_BOOLEAN_TRUE; if(safe_str_eq(crm_op, CRM_OP_REPROBE)) { cib_options = cib_none; probed = XML_BOOLEAN_FALSE; } update_attrd(NULL, CRM_OP_PROBED, probed); } else if(operation != NULL) { lrm_rsc_t *rsc = NULL; xmlNode *params = NULL; xmlNode *xml_rsc = find_xml_node( input->xml, XML_CIB_TAG_RESOURCE, TRUE); CRM_CHECK(xml_rsc != NULL, return); /* only the first 16 chars are used by the LRM */ params = find_xml_node(input->xml, XML_TAG_ATTRS, TRUE); if(safe_str_eq(operation, CRMD_ACTION_DELETE)) { create_rsc = FALSE; } rsc = get_lrm_resource(xml_rsc, input->xml, create_rsc); if(rsc == NULL && create_rsc) { crm_err("Invalid resource definition"); crm_log_xml_warn(input->msg, "bad input"); } else if(rsc == NULL) { lrm_op_t* op = NULL; crm_notice("Not creating resource for a %s event: %s", operation, ID(input->xml)); op = construct_op(input->xml, ID(xml_rsc), operation); op->op_status = LRM_OP_DONE; op->rc = EXECRA_OK; CRM_ASSERT(op != NULL); send_direct_ack(from_host, from_sys, NULL, op, ID(xml_rsc)); free_lrm_op(op); } else if(safe_str_eq(operation, CRMD_ACTION_CANCEL)) { lrm_op_t* op = NULL; char *op_key = NULL; char *meta_key = NULL; int call = 0; const char *call_id = NULL; const char *op_task = NULL; const char *op_interval = NULL; CRM_CHECK(params != NULL, crm_log_xml_warn(input->xml, "Bad command"); return); meta_key = crm_meta_name(XML_LRM_ATTR_INTERVAL); op_interval = crm_element_value(params, meta_key); crm_free(meta_key); meta_key = crm_meta_name(XML_LRM_ATTR_TASK); op_task = crm_element_value(params, meta_key); crm_free(meta_key); meta_key = crm_meta_name(XML_LRM_ATTR_CALLID); call_id = crm_element_value(params, meta_key); crm_free(meta_key); CRM_CHECK(op_task != NULL, crm_log_xml_warn(input->xml, "Bad command"); return); CRM_CHECK(op_interval != NULL, crm_log_xml_warn(input->xml, "Bad command"); return); op = construct_op(input->xml, rsc->id, op_task); CRM_ASSERT(op != NULL); op_key = generate_op_key( rsc->id,op_task,crm_parse_int(op_interval,"0")); crm_debug("PE requested op %s (call=%s) be cancelled", op_key, call_id?call_id:"NA"); call = crm_parse_int(call_id, "0"); if(call == 0) { /* the normal case when the PE cancels a recurring op */ done = cancel_op_key(rsc, op_key, TRUE); } else { /* the normal case when the PE cancels an orphan op */ done = cancel_op(rsc, NULL, call, TRUE); } if(done == FALSE) { crm_debug("Nothing known about operation %d for %s", call, op_key); delete_op_entry(NULL, rsc->id, op_key, call); /* needed?? surely not otherwise the cancel_op_(_key) wouldn't * have failed in the first place */ g_hash_table_remove(pending_ops, op_key); } op->rc = EXECRA_OK; op->op_status = LRM_OP_DONE; send_direct_ack(from_host, from_sys, rsc, op, rsc->id); crm_free(op_key); free_lrm_op(op); } else if(safe_str_eq(operation, CRMD_ACTION_DELETE)) { int rc = HA_OK; CRM_ASSERT(rsc != NULL); crm_info("Removing resource %s from the LRM", rsc->id); rc = fsa_lrm_conn->lrm_ops->delete_rsc(fsa_lrm_conn, rsc->id); if(rc == HA_OK) { crm_info("Resource '%s' deleted for %s on %s", rsc->id, from_sys, from_host); delete_rsc_entry(input, rsc->id, rc); #ifdef HAVE_LRM_OP_T_RSC_DELETED } else if(rc == HA_RSCBUSY) { struct pending_deletion_op_s *op = NULL; crm_info("Resource deletion scheduled for %s on %s", from_sys, from_host); crm_malloc0(op, sizeof(struct pending_deletion_op_s)); op->rsc = crm_strdup(rsc->id); op->input = copy_ha_msg_input(input); g_hash_table_insert( deletion_ops, crm_element_value_copy(input->msg, XML_ATTR_REFERENCE), op); #endif } else { crm_err("Deletion of resource '%s' for %s on %s failed: %d", rsc->id, from_sys, from_host, rc); delete_rsc_entry(input, rsc->id, rc); } } else if(rsc != NULL) { do_lrm_rsc_op(rsc, operation, input->xml, input->msg); } lrm_free_rsc(rsc); } else { crm_err("Operation was neither a lrm_query, nor a rsc op. %s", crm_str(crm_op)); register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL); } } lrm_op_t * construct_op(xmlNode *rsc_op, const char *rsc_id, const char *operation) { lrm_op_t *op = NULL; const char *op_delay = NULL; const char *op_timeout = NULL; const char *op_interval = NULL; GHashTable *params = NULL; const char *transition = NULL; CRM_LOG_ASSERT(rsc_id != NULL); crm_malloc0(op, sizeof(lrm_op_t)); op->op_type = crm_strdup(operation); op->op_status = LRM_OP_PENDING; op->rc = -1; op->rsc_id = crm_strdup(rsc_id); op->interval = 0; op->timeout = 0; op->start_delay = 0; op->copyparams = 0; op->app_name = crm_strdup(CRM_SYSTEM_CRMD); if(rsc_op == NULL) { CRM_LOG_ASSERT(safe_str_eq(CRMD_ACTION_STOP, operation)); op->user_data = NULL; op->user_data_len = 0; /* the stop_all_resources() case * by definition there is no DC (or they'd be shutting * us down). * So we should put our version here. */ op->params = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); g_hash_table_insert(op->params, crm_strdup(XML_ATTR_CRM_VERSION), crm_strdup(CRM_FEATURE_SET)); crm_debug_2("Constructed %s op for %s", operation, rsc_id); return op; } params = xml2list(rsc_op); g_hash_table_remove(params, CRM_META"_op_target_rc"); op_delay = crm_meta_value(params, XML_OP_ATTR_START_DELAY); op_timeout = crm_meta_value(params, XML_ATTR_TIMEOUT); op_interval = crm_meta_value(params, XML_LRM_ATTR_INTERVAL); op->interval = crm_parse_int(op_interval, "0"); op->timeout = crm_parse_int(op_timeout, "0"); op->start_delay = crm_parse_int(op_delay, "0"); if(safe_str_neq(operation, RSC_STOP)) { op->params = params; } else { /* Create a blank parameter list so that we stop the resource * with the old attributes, not the new ones */ const char *version = g_hash_table_lookup(params, XML_ATTR_CRM_VERSION); op->params = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); if(version) { g_hash_table_insert(op->params, crm_strdup(XML_ATTR_CRM_VERSION), crm_strdup(version)); } g_hash_table_destroy(params); params = NULL; } /* sanity */ if(op->interval < 0) { op->interval = 0; } if(op->timeout <= 0) { op->timeout = op->interval; } if(op->start_delay < 0) { op->start_delay = 0; } transition = crm_element_value(rsc_op, XML_ATTR_TRANSITION_KEY); CRM_CHECK(transition != NULL, return op); op->user_data = crm_strdup(transition); op->user_data_len = 1+strlen(op->user_data); if(op->interval != 0) { if(safe_str_eq(operation, CRMD_ACTION_START) || safe_str_eq(operation, CRMD_ACTION_STOP)) { crm_err("Start and Stop actions cannot have an interval: %d", op->interval); op->interval = 0; } } /* reset the resource's parameters? */ if(op->interval == 0) { if(safe_str_eq(CRMD_ACTION_START, operation) || safe_str_eq(CRMD_ACTION_STATUS, operation)) { op->copyparams = 1; } } crm_debug_2("Constructed %s op for %s: interval=%d", operation, rsc_id, op->interval); return op; } void send_direct_ack(const char *to_host, const char *to_sys, lrm_rsc_t *rsc, lrm_op_t* op, const char *rsc_id) { xmlNode *reply = NULL; xmlNode *update, *iter; xmlNode *fragment; CRM_CHECK(op != NULL, return); if(op->rsc_id == NULL) { CRM_LOG_ASSERT(rsc_id != NULL); op->rsc_id = crm_strdup(rsc_id); } if(to_sys == NULL) { to_sys = CRM_SYSTEM_TENGINE; } update = create_node_state( fsa_our_uname, NULL, NULL, NULL, NULL, NULL, FALSE, __FUNCTION__); iter = create_xml_node(update, XML_CIB_TAG_LRM); crm_xml_add(iter, XML_ATTR_ID, fsa_our_uuid); iter = create_xml_node(iter, XML_LRM_TAG_RESOURCES); iter = create_xml_node(iter, XML_LRM_TAG_RESOURCE); crm_xml_add(iter, XML_ATTR_ID, op->rsc_id); build_operation_update(iter, rsc, op, __FUNCTION__, 0, LOG_DEBUG); fragment = create_cib_fragment(update, XML_CIB_TAG_STATUS); reply = create_request(CRM_OP_INVOKE_LRM, fragment, to_host, to_sys, CRM_SYSTEM_LRMD, NULL); crm_log_xml_debug_2(update, "ACK Update"); crm_info("ACK'ing resource op %s_%s_%d from %s: %s", op->rsc_id, op->op_type, op->interval, op->user_data, crm_element_value(reply, XML_ATTR_REFERENCE)); if(relay_message(reply, TRUE) == FALSE) { crm_log_xml(LOG_ERR, "Unable to route reply", reply); } free_xml(fragment); free_xml(update); free_xml(reply); } static gboolean stop_recurring_action_by_rsc(gpointer key, gpointer value, gpointer user_data) { lrm_rsc_t *rsc = user_data; struct recurring_op_s *op = (struct recurring_op_s*)value; if(op->interval != 0 && safe_str_eq(op->rsc_id, rsc->id)) { if (cancel_op(rsc, key, op->call_id, FALSE) == FALSE) { return TRUE; } } return FALSE; } void do_lrm_rsc_op(lrm_rsc_t *rsc, const char *operation, xmlNode *msg, xmlNode *request) { int call_id = 0; char *op_id = NULL; lrm_op_t* op = NULL; fsa_data_t *msg_data = NULL; const char *transition = NULL; CRM_CHECK(rsc != NULL, return); if(msg != NULL) { transition = crm_element_value(msg, XML_ATTR_TRANSITION_KEY); if(transition == NULL) { crm_log_xml_err(msg, "Missing transition number"); } } op = construct_op(msg, rsc->id, operation); /* stop the monitor before stopping the resource */ if(crm_str_eq(operation, CRMD_ACTION_STOP, TRUE) || crm_str_eq(operation, CRMD_ACTION_DEMOTE, TRUE) || crm_str_eq(operation, CRMD_ACTION_PROMOTE, TRUE) || crm_str_eq(operation, CRMD_ACTION_MIGRATE, TRUE)) { g_hash_table_foreach_remove(pending_ops, stop_recurring_action_by_rsc, rsc); } /* now do the op */ crm_info("Performing key=%s op=%s_%s_%d )", transition, rsc->id, operation, op->interval); if(fsa_state != S_NOT_DC && fsa_state != S_POLICY_ENGINE && fsa_state != S_TRANSITION_ENGINE) { if(safe_str_neq(operation, "fail") && safe_str_neq(operation, CRMD_ACTION_STOP)) { crm_info("Discarding attempt to perform action %s on %s" " in state %s", operation, rsc->id, fsa_state2string(fsa_state)); op->rc = 99; op->op_status = LRM_OP_ERROR; send_direct_ack(NULL, NULL, rsc, op, rsc->id); free_lrm_op(op); crm_free(op_id); return; } } op_id = generate_op_key(rsc->id, op->op_type, op->interval); if(op->interval > 0) { /* cancel it so we can then restart it without conflict */ cancel_op_key(rsc, op_id, FALSE); op->target_rc = CHANGED; } else { op->target_rc = EVERYTIME; } g_hash_table_replace(resources,crm_strdup(rsc->id), crm_strdup(op_id)); call_id = rsc->ops->perform_op(rsc, op); if(call_id <= 0) { crm_err("Operation %s on %s failed: %d", operation, rsc->id, call_id); register_fsa_error(C_FSA_INTERNAL, I_FAIL, NULL); } else { /* record all operations so we can wait * for them to complete during shutdown */ char *call_id_s = make_stop_id(rsc->id, call_id); struct recurring_op_s *pending = NULL; crm_malloc0(pending, sizeof(struct recurring_op_s)); crm_debug_2("Recording pending op: %d - %s %s", call_id, op_id, call_id_s); pending->call_id = call_id; pending->interval = op->interval; pending->op_key = crm_strdup(op_id); pending->rsc_id = crm_strdup(rsc->id); g_hash_table_replace(pending_ops, call_id_s, pending); if(op->interval > 0 && op->start_delay > START_DELAY_THRESHOLD) { char *uuid = NULL; int dummy = 0, target_rc = 0; crm_info("Faking confirmation of %s: execution postponed for over 5 minutes", op_id); decode_transition_key(op->user_data, &uuid, &dummy, &dummy, &target_rc); crm_free(uuid); op->rc = target_rc; op->op_status = LRM_OP_DONE; send_direct_ack(NULL, NULL, rsc, op, rsc->id); } } crm_free(op_id); free_lrm_op(op); return; } void free_deletion_op(gpointer value) { struct pending_deletion_op_s *op = value; crm_free(op->rsc); delete_ha_msg_input(op->input); crm_free(op); } void free_recurring_op(gpointer value) { struct recurring_op_s *op = (struct recurring_op_s*)value; crm_free(op->rsc_id); crm_free(op->op_key); crm_free(op); } static void dup_attr(gpointer key, gpointer value, gpointer user_data) { g_hash_table_replace(user_data, crm_strdup(key), crm_strdup(value)); } lrm_op_t * copy_lrm_op(const lrm_op_t *op) { lrm_op_t *op_copy = NULL; CRM_CHECK(op != NULL, return NULL); CRM_CHECK(op->rsc_id != NULL, return NULL); crm_malloc0(op_copy, sizeof(lrm_op_t)); op_copy->op_type = crm_strdup(op->op_type); /* input fields */ op_copy->params = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); if(op->params != NULL) { g_hash_table_foreach(op->params, dup_attr, op_copy->params); } op_copy->timeout = op->timeout; op_copy->interval = op->interval; op_copy->target_rc = op->target_rc; /* in the CRM, this is always a string */ if(op->user_data != NULL) { op_copy->user_data = crm_strdup(op->user_data); } /* output fields */ op_copy->op_status = op->op_status; op_copy->rc = op->rc; op_copy->call_id = op->call_id; op_copy->output = NULL; op_copy->rsc_id = crm_strdup(op->rsc_id); if(op->app_name != NULL) { op_copy->app_name = crm_strdup(op->app_name); } if(op->output != NULL) { op_copy->output = crm_strdup(op->output); } return op_copy; } lrm_rsc_t * copy_lrm_rsc(const lrm_rsc_t *rsc) { lrm_rsc_t *rsc_copy = NULL; if(rsc == NULL) { return NULL; } crm_malloc0(rsc_copy, sizeof(lrm_rsc_t)); rsc_copy->id = crm_strdup(rsc->id); rsc_copy->type = crm_strdup(rsc->type); rsc_copy->class = NULL; rsc_copy->provider = NULL; if(rsc->class != NULL) { rsc_copy->class = crm_strdup(rsc->class); } if(rsc->provider != NULL) { rsc_copy->provider = crm_strdup(rsc->provider); } /* GHashTable* params; */ rsc_copy->params = NULL; rsc_copy->ops = NULL; return rsc_copy; } void cib_rsc_callback(xmlNode *msg, int call_id, int rc, xmlNode *output, void *user_data) { switch(rc) { case cib_ok: case cib_diff_failed: case cib_diff_resync: crm_debug_2("Resource update %d complete: rc=%d", call_id, rc); break; default: crm_warn("Resource update %d failed: (rc=%d) %s", call_id, rc, cib_error2string(rc)); } } int do_update_resource(lrm_op_t* op) { /* */ int rc = cib_ok; lrm_rsc_t *rsc = NULL; xmlNode *update, *iter = NULL; int call_opt = cib_quorum_override; CRM_CHECK(op != NULL, return 0); if(fsa_state == S_ELECTION || fsa_state == S_PENDING) { crm_info("Sending update to local CIB in state: %s", fsa_state2string(fsa_state)); call_opt |= cib_scope_local; } iter = create_xml_node(iter, XML_CIB_TAG_STATUS); update = iter; iter = create_xml_node(iter, XML_CIB_TAG_STATE); set_uuid(iter, XML_ATTR_UUID, fsa_our_uname); crm_xml_add(iter, XML_ATTR_UNAME, fsa_our_uname); crm_xml_add(iter, XML_ATTR_ORIGIN, __FUNCTION__); iter = create_xml_node(iter, XML_CIB_TAG_LRM); crm_xml_add(iter, XML_ATTR_ID, fsa_our_uuid); iter = create_xml_node(iter, XML_LRM_TAG_RESOURCES); iter = create_xml_node(iter, XML_LRM_TAG_RESOURCE); crm_xml_add(iter, XML_ATTR_ID, op->rsc_id); rsc = fsa_lrm_conn->lrm_ops->get_rsc(fsa_lrm_conn, op->rsc_id); build_operation_update(iter, rsc, op, __FUNCTION__, 0, LOG_DEBUG); if(rsc) { crm_xml_add(iter, XML_ATTR_TYPE, rsc->type); crm_xml_add(iter, XML_AGENT_ATTR_CLASS, rsc->class); crm_xml_add(iter, XML_AGENT_ATTR_PROVIDER,rsc->provider); CRM_CHECK(rsc->type != NULL, crm_err("Resource %s has no value for type", op->rsc_id)); CRM_CHECK(rsc->class != NULL, crm_err("Resource %s has no value for class", op->rsc_id)); lrm_free_rsc(rsc); } else { crm_warn("Resource %s no longer exists in the lrmd", op->rsc_id); goto cleanup; } /* make it an asyncronous call and be done with it * * Best case: * the resource state will be discovered during * the next signup or election. * * Bad case: * we are shutting down and there is no DC at the time, * but then why were we shutting down then anyway? * (probably because of an internal error) * * Worst case: * we get shot for having resources "running" when the really weren't * * the alternative however means blocking here for too long, which * isnt acceptable */ fsa_cib_update(XML_CIB_TAG_STATUS, update, call_opt, rc); /* the return code is a call number, not an error code */ crm_debug_2("Sent resource state update message: %d", rc); fsa_cib_conn->cmds->register_callback( fsa_cib_conn, rc, 60, FALSE, NULL, "cib_rsc_callback", cib_rsc_callback); cleanup: free_xml(update); return rc; } void do_lrm_event(long long action, enum crmd_fsa_cause cause, enum crmd_fsa_state cur_state, enum crmd_fsa_input cur_input, fsa_data_t *msg_data) { CRM_CHECK(FALSE, return); } gboolean process_lrm_event(lrm_op_t *op) { char *op_id = NULL; char *op_key = NULL; int update_id = 0; int log_level = LOG_ERR; gboolean removed = FALSE; struct recurring_op_s *pending = NULL; CRM_CHECK(op != NULL, return FALSE); CRM_CHECK(op->rsc_id != NULL, return FALSE); op_key = generate_op_key(op->rsc_id, op->op_type, op->interval); switch(op->op_status) { case LRM_OP_ERROR: case LRM_OP_PENDING: case LRM_OP_NOTSUPPORTED: break; case LRM_OP_CANCELLED: log_level = LOG_INFO; break; case LRM_OP_DONE: log_level = LOG_INFO; break; case LRM_OP_TIMEOUT: log_level = LOG_DEBUG_3; crm_err("LRM operation %s (%d) %s (timeout=%dms)", op_key, op->call_id, op_status2text(op->op_status), op->timeout); break; default: crm_err("Mapping unknown status (%d) to ERROR", op->op_status); op->op_status = LRM_OP_ERROR; } if(op->op_status == LRM_OP_ERROR && (op->rc == EXECRA_RUNNING_MASTER || op->rc == EXECRA_NOT_RUNNING)) { /* Leave it up to the TE/PE to decide if this is an error */ op->op_status = LRM_OP_DONE; log_level = LOG_INFO; } op_id = make_stop_id(op->rsc_id, op->call_id); pending = g_hash_table_lookup(pending_ops, op_id); if(op->op_status != LRM_OP_CANCELLED) { update_id = do_update_resource(op); if(op->interval != 0) { goto out; } } else if(op->interval == 0) { /* This will occur when "crm resource cleanup" is called while actions are in-flight */ crm_err("Op %s (call=%d): Cancelled", op_key, op->call_id); send_direct_ack(NULL, NULL, NULL, op, op->rsc_id); } else if(pending == NULL) { crm_err("Op %s (call=%d): No 'pending' entry", op_key, op->call_id); } else if(op->user_data == NULL) { crm_err("Op %s (call=%d): No user data", op_key, op->call_id); } else if(pending->remove) { delete_op_entry(op, op->rsc_id, op_key, op->call_id); } else { /* Before a stop is called, no need to direct ack */ crm_debug_2("Op %s (call=%d): no delete event required", op_key, op->call_id); } if(g_hash_table_remove(pending_ops, op_id)) { removed = TRUE; crm_debug_2("Op %s (call=%d, stop-id=%s): Confirmed", op_key, op->call_id, op_id); } out: if(op->op_status == LRM_OP_DONE) { do_crm_log(log_level, "LRM operation %s (call=%d, rc=%d, cib-update=%d, confirmed=%s) %s", op_key, op->call_id, op->rc, update_id, removed?"true":"false", execra_code2string(op->rc)); } else { do_crm_log(log_level, "LRM operation %s (call=%d, status=%d, cib-update=%d, confirmed=%s) %s", op_key, op->call_id, op->op_status, update_id, removed?"true":"false", op_status2text(op->op_status)); } if(op->rc != 0 && op->output != NULL) { crm_info("Result: %s", op->output); } else if(op->output != NULL) { crm_debug("Result: %s", op->output); } #ifdef HAVE_LRM_OP_T_RSC_DELETED if(op->rsc_deleted) { crm_info("Deletion of resource '%s' complete after %s", op->rsc_id, op_key); delete_rsc_entry(NULL, op->rsc_id, HA_OK); } #endif /* If a shutdown was escalated while operations were pending, * then the FSA will be stalled right now... allow it to continue */ mainloop_set_trigger(fsa_source); crm_free(op_key); crm_free(op_id); return TRUE; } char * make_stop_id(const char *rsc, int call_id) { char *op_id = NULL; crm_malloc0(op_id, strlen(rsc) + 34); if(op_id != NULL) { snprintf(op_id, strlen(rsc) + 34, "%s:%d", rsc, call_id); } return op_id; } diff --git a/fencing/commands.c b/fencing/commands.c index 25f770e07f..92d7eea365 100644 --- a/fencing/commands.c +++ b/fencing/commands.c @@ -1,956 +1,956 @@ /* * Copyright (C) 2009 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include GHashTable *device_list = NULL; static int active_children = 0; static void exec_child_done(ProcTrack* proc, int status, int signo, int rc, int waslogged); static void exec_child_new(ProcTrack* p) { active_children++; } static const char *exec_child_name(ProcTrack* p) { async_command_t *cmd = proctrack_data(p); return cmd->client?cmd->client:cmd->remote; } static ProcTrack_ops StonithdProcessTrackOps = { exec_child_done, exec_child_new, exec_child_name, }; static void free_async_command(async_command_t *cmd) { if(cmd->node_attrs) { g_hash_table_destroy(cmd->node_attrs); } crm_free(cmd->action); crm_free(cmd->victim); crm_free(cmd->remote); crm_free(cmd->client); crm_free(cmd->origin); crm_free(cmd->op); crm_free(cmd); } static async_command_t *create_async_command(xmlNode *msg, const char *action) { async_command_t *cmd = NULL; CRM_CHECK(action != NULL, crm_log_xml_warn(msg, "NoAction"); return NULL); crm_malloc0(cmd, sizeof(async_command_t)); crm_element_value_int(msg, F_STONITH_CALLID, &(cmd->id)); crm_element_value_int(msg, F_STONITH_CALLOPTS, &(cmd->options)); crm_element_value_int(msg, F_STONITH_TIMEOUT, &(cmd->timeout)); cmd->origin = crm_element_value_copy(msg, F_ORIG); cmd->remote = crm_element_value_copy(msg, F_STONITH_REMOTE); cmd->client = crm_element_value_copy(msg, F_STONITH_CLIENTID); cmd->op = crm_element_value_copy(msg, F_STONITH_OPERATION); cmd->action = crm_strdup(action); cmd->victim = crm_element_value_copy(msg, F_STONITH_TARGET); cmd->pt_ops = &StonithdProcessTrackOps; CRM_CHECK(cmd->op != NULL, crm_log_xml_warn(msg, "NoOp"); free_async_command(cmd); return NULL); CRM_CHECK(cmd->client != NULL || cmd->remote != NULL, crm_log_xml_warn(msg, "NoClient")); return cmd; } static void free_device(gpointer data) { stonith_device_t *device = data; g_hash_table_destroy(device->params); g_hash_table_destroy(device->aliases); - slist_destroy(char, item, device->targets, crm_free(item)); + slist_basic_destroy(device->targets); crm_free(device->namespace); crm_free(device->agent); crm_free(device->id); crm_free(device); } static GHashTable *build_port_aliases(const char *hostmap, GListPtr *targets) { char *name = NULL; int last = 0, lpc = 0, max = 0; GHashTable *aliases = g_hash_table_new_full(g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); if(hostmap == NULL) { return aliases; } max = strlen(hostmap); for(; lpc < max; lpc++) { if(hostmap[lpc] == 0) { break; } else if(isalpha(hostmap[lpc])) { /* keep going */ } else if(hostmap[lpc] == '=') { crm_free(name); crm_malloc0(name, 1 + lpc - last); strncpy(name, hostmap + last, lpc - last); last = lpc + 1; } else if(name && isspace(hostmap[lpc])) { char *value = NULL; crm_malloc0(value, 1 + lpc - last); strncpy(value, hostmap + last, lpc - last); last = lpc + 1; crm_debug("Adding alias '%s'='%s'", name, value); g_hash_table_replace(aliases, name, value); if(targets) { *targets = g_list_append(*targets, crm_strdup(value)); } value=NULL; name=NULL; } else if(isspace(hostmap[lpc])) { last = lpc; } } crm_free(name); return aliases; } static void parse_host_line(const char *line, GListPtr *output) { int lpc = 0; int max = 0; int last = 0; if(line) { max = strlen(line); } else { return; } /* Check for any complaints about additional parameters that the device doesn't understand */ if(strstr(line, "invalid") || strstr(line, "variable")) { crm_debug("Skipping: %s", line); return; } crm_debug_2("Processing: %s", line); /* Skip initial whitespace */ for(lpc = 0; lpc <= max && isspace(line[lpc]); lpc++) { last = lpc+1; } /* Now the actual content */ for(lpc = 0; lpc <= max; lpc++) { gboolean a_space = isspace(line[lpc]); if(a_space && lpc < max && isspace(line[lpc+1])) { /* fast-forward to the end of the spaces */ } else if(a_space || line[lpc] == ',' || line[lpc] == 0) { int rc = 0; char *entry = NULL; crm_malloc0(entry, 1 + lpc - last); rc = sscanf(line+last, "%[a-zA-Z0-9_-.]", entry); if(rc != 1) { crm_warn("Could not parse (%d %d): %s", last, lpc, line+last); } else if(safe_str_neq(entry, "on") && safe_str_neq(entry, "off")) { crm_debug_2("Adding '%s'", entry); *output = g_list_append(*output, entry); entry = NULL; } crm_free(entry); last = lpc + 1; } } } static GListPtr parse_host_list(const char *hosts) { int lpc = 0; int max = 0; int last = 0; GListPtr output = NULL; if(hosts == NULL) { return output; } max = strlen(hosts); for(lpc = 0; lpc <= max; lpc++) { if(hosts[lpc] == '\n' || hosts[lpc] == 0) { char *line = NULL; crm_malloc0(line, 2 + lpc - last); snprintf(line, 1 + lpc - last, "%s", hosts+last); parse_host_line(line, &output); crm_free(line); last = lpc + 1; } } return output; } static stonith_device_t *build_device_from_xml(xmlNode *msg) { xmlNode *dev = get_xpath_object("//"F_STONITH_DEVICE, msg, LOG_ERR); stonith_device_t *device = NULL; crm_malloc0(device, sizeof(stonith_device_t)); device->id = crm_element_value_copy(dev, XML_ATTR_ID); device->agent = crm_element_value_copy(dev, "agent"); device->namespace = crm_element_value_copy(dev, "namespace"); device->params = xml2list(dev); /* TODO: Hook up priority */ return device; } static int stonith_device_register(xmlNode *msg) { const char *value = NULL; stonith_device_t *device = build_device_from_xml(msg); value = g_hash_table_lookup(device->params, STONITH_ATTR_HOSTLIST); if(value) { device->targets = parse_host_list(value); } value = g_hash_table_lookup(device->params, STONITH_ATTR_HOSTMAP); device->aliases = build_port_aliases(value, &(device->targets)); g_hash_table_replace(device_list, device->id, device); crm_info("Added '%s' to the device list (%d active devices)", device->id, g_hash_table_size(device_list)); return stonith_ok; } static int stonith_device_remove(xmlNode *msg) { xmlNode *dev = get_xpath_object("//"F_STONITH_DEVICE, msg, LOG_ERR); const char *id = crm_element_value(dev, XML_ATTR_ID); if(g_hash_table_remove(device_list, id)) { crm_info("Removed '%s' from the device list (%d active devices)", id, g_hash_table_size(device_list)); } else { crm_info("Device '%s' not found (%d active devices)", id, g_hash_table_size(device_list)); } return stonith_ok; } static gboolean string_in_list(GListPtr list, const char *item) { int lpc = 0; int max = g_list_length(list); for(lpc = 0; lpc < max; lpc ++) { const char *value = g_list_nth_data(list, lpc); if(safe_str_eq(item, value)) { return TRUE; } } return FALSE; } static const char *get_victim_name(stonith_device_t *dev, const char *host) { if(dev == NULL) { return NULL; } else if(host && dev->aliases) { char *alias = g_hash_table_lookup(dev->aliases, host); if(alias) { return alias; } } return host; } static int stonith_device_action(xmlNode *msg, char **output) { int rc = stonith_ok; xmlNode *dev = get_xpath_object("//"F_STONITH_DEVICE, msg, LOG_ERR); const char *id = crm_element_value(dev, F_STONITH_DEVICE); const char *action = crm_element_value(dev, F_STONITH_ACTION); async_command_t *cmd = NULL; stonith_device_t *device = NULL; if(id) { crm_debug_2("Looking for '%s'", id); device = g_hash_table_lookup(device_list, id); } else { CRM_CHECK(safe_str_eq(action, "metadata"), crm_log_xml_warn(msg, "StrangeOp")); device = build_device_from_xml(msg); if(device != NULL && device->id == NULL) { device->id = crm_strdup(device->agent); } } if(device) { int exec_rc = 0; const char *victim = NULL; GHashTable *node_attrs = xml2list(dev); cmd = create_async_command(msg, action); if(cmd == NULL) { free_device(device); return st_err_internal; } cmd->node_attrs = node_attrs; victim = get_victim_name(device, cmd->victim); if(cmd->victim && victim == NULL) { crm_err("Unknown or unhandled port '%s' for device '%s'", cmd->victim, device->id); free_async_command(cmd); return st_err_unknown_port; } cmd->device = crm_strdup(device->id); crm_debug("Calling '%s' with action '%s'%s%s", device->id, action, victim?" on port ":"", victim?victim:""); exec_rc = run_stonith_agent( device->agent, device->params, cmd->node_attrs, action, victim, &rc, output, cmd); if(exec_rc < 0 || rc != 0) { crm_warn("Operation %s on %s failed (%d/%d): %.100s", action, device->id, exec_rc, rc, *output); } else if(exec_rc > 0) { crm_debug("Operation %s on %s active with pid: %d", action, device->id, exec_rc); rc = exec_rc; } else { crm_info("Operation %s on %s passed: %.100s", action, device->id, *output); } } else { crm_notice("Device %s not found", id); rc = st_err_unknown_device; } if(id == NULL) { free_device(device); } return rc; } static gboolean can_fence_host_with_device(stonith_device_t *dev, const char *host) { gboolean can = FALSE; const char *victim = NULL; const char *check_type = NULL; if(dev == NULL) { return FALSE; } else if(host == NULL) { return TRUE; } victim = get_victim_name(dev, host); check_type = g_hash_table_lookup(dev->params, STONITH_ATTR_HOSTCHECK); if(check_type == NULL) { if(g_hash_table_lookup(dev->params, STONITH_ATTR_HOSTLIST)) { check_type = "static-list"; } else { check_type = "dynamic-list"; } } if(safe_str_eq(check_type, "none")) { can = TRUE; } else if(safe_str_eq(check_type, "static-list")) { /* Presence in the hostmap is sufficient * Only use if all hosts on which the device can be active can always fence all listed hosts */ if(string_in_list(dev->targets, victim)) { can = TRUE; } } else if(safe_str_eq(check_type, "dynamic-list")) { time_t now = time(NULL); /* Host/alias must be in the list output to be eligable to be fenced * * Will cause problems if down'd nodes aren't listed or (for virtual nodes) * if the guest is still listed despite being moved to another machine */ if(dev->targets == NULL || dev->targets_age + 60 < now) { char *output = NULL; int rc = stonith_ok; int exec_rc = stonith_ok; /* Some use hostlist instead of the "standard" list */ const char *list_cmd = g_hash_table_lookup(dev->params, STONITH_ATTR_LIST_OP); if(list_cmd == NULL) { list_cmd = "list"; } /* Check for the target's presence in the output of the 'list' command */ - slist_destroy(char, item, dev->targets, crm_free(item)); + slist_basic_destroy(dev->targets); dev->targets = NULL; exec_rc = run_stonith_agent(dev->agent, dev->params, NULL, list_cmd, NULL, &rc, &output, NULL); if(exec_rc < 0 || rc != 0) { crm_notice("Disabling port list queries for %s (%d/%d): %s", dev->id, exec_rc, rc, output); dev->targets_age = -1; } else { crm_info("Refreshing port list for %s", dev->id); dev->targets = parse_host_list(output); dev->targets_age = now; } crm_free(output); } if(string_in_list(dev->targets, victim)) { can = TRUE; } } else if(safe_str_eq(check_type, "status")) { int rc = 0; int exec_rc = 0; /* Some use stat instead of the "standard" status */ const char *status = g_hash_table_lookup(dev->params, STONITH_ATTR_STATUS_OP); if(status == NULL) { status = "status"; } /* Run the status operation for the device/target combination * Will cause problems if the device doesn't return 2 for down'd nodes or * (for virtual nodes) if the device doesn't return 1 for guests that * have been moved to another host */ /* TODO: Get node_attrs in here */ exec_rc = run_stonith_agent( dev->agent, dev->params, NULL, status, victim, &rc, NULL, NULL); if(exec_rc != 0) { crm_err("Could not invoke %s: rc=%d", dev->id, exec_rc); } else if(rc == 1 /* unkown */) { crm_debug_2("Host %s is not known by %s", victim, dev->id); } else if(rc == 0 /* active */ || rc == 2 /* inactive */) { can = TRUE; } else { crm_err("Unkown result calling %s for %s with %s: rc=%d", status, victim, dev->id, rc); } } else { crm_err("Unknown check type: %s", check_type); } crm_info("%s can%s fence %s: %s", dev->id, can?"":" not", victim, check_type); return can; } struct device_search_s { const char *host; GListPtr capable; }; static void search_devices( gpointer key, gpointer value, gpointer user_data) { stonith_device_t *dev = value; struct device_search_s *search = user_data; if(can_fence_host_with_device(dev, search->host)) { search->capable = g_list_append(search->capable, value); } } static int stonith_query(xmlNode *msg, xmlNode **list) { struct device_search_s search; int available_devices = 0; xmlNode *dev = get_xpath_object("//@"F_STONITH_TARGET, msg, LOG_DEBUG_3); search.host = NULL; search.capable = NULL; if(dev) { search.host = crm_element_value(dev, F_STONITH_TARGET); } crm_log_xml_info(msg, "Query"); g_hash_table_foreach(device_list, search_devices, &search); available_devices = g_list_length(search.capable); if(search.host) { crm_info("Found %d matching devices for '%s'", available_devices, search.host); } else { crm_info("%d devices installed", available_devices); } /* Pack the results into data */ if(list) { GListPtr lpc = NULL; *list = create_xml_node(NULL, __FUNCTION__); crm_xml_add(*list, F_STONITH_TARGET, search.host); crm_xml_add_int(*list, "st-available-devices", available_devices); for(lpc = search.capable; lpc != NULL; lpc = lpc->next) { stonith_device_t *device = (stonith_device_t*)lpc->data; dev = create_xml_node(*list, F_STONITH_DEVICE); crm_xml_add(dev, XML_ATTR_ID, device->id); crm_xml_add(dev, "namespace", device->namespace); crm_xml_add(dev, "agent", device->agent); if(search.host == NULL) { xmlNode *attrs = create_xml_node(dev, XML_TAG_ATTRS); g_hash_table_foreach(device->params, hash2field, attrs); } } } g_list_free(search.capable); return available_devices; } static void log_operation(async_command_t *cmd, int rc, int pid, const char *next, const char *output) { if(rc == 0) { next = NULL; } if(cmd->victim != NULL) { do_crm_log(rc==0?LOG_INFO:LOG_ERR, "Operation '%s' [%d] for host '%s' with device '%s' returned: %d%s%s (call %d from %s)", cmd->action, pid, cmd->victim, cmd->device, rc, next?". Trying: ":"", next?next:"", cmd->id, cmd->client); } else { do_crm_log(rc==0?LOG_DEBUG:LOG_NOTICE, "Operation '%s' [%d] for device '%s' returned: %d%s%s", cmd->action, pid, cmd->device, rc, next?". Trying: ":"", next?next:""); } if(output) { /* Logging the whole string confuses syslog when the string is xml */ char *local_copy = crm_strdup(output); int lpc = 0, last = 0, more = strlen(local_copy); for(lpc = 0; lpc < more; lpc++) { if(local_copy[lpc] == '\n' || local_copy[lpc] == 0) { local_copy[lpc] = 0; crm_debug("%s output: %s", cmd->device, local_copy+last); last = lpc+1; } } crm_debug("%s output: %s (total %d bytes)", cmd->device, local_copy+last, more); crm_free(local_copy); } } #define READ_MAX 500 static void exec_child_done(ProcTrack* proc, int status, int signum, int rc, int waslogged) { int len = 0; int more = 0; gboolean bcast = FALSE; char *output = NULL; xmlNode *data = NULL; xmlNode *reply = NULL; int pid = proctrack_pid(proc); async_command_t *cmd = proctrack_data(proc); CRM_CHECK(cmd != NULL, return); active_children--; if( signum ) { rc = st_err_signal; if( proctrack_timedout(proc) ) { crm_warn("Child '%d' performing action '%s' with '%s' timed out", pid, cmd->action, cmd->device); rc = st_err_timeout; } } do { char buffer[READ_MAX]; errno = 0; memset(&buffer, 0, READ_MAX); more = read(cmd->stdout, buffer, READ_MAX-1); do_crm_log(status!=0?LOG_DEBUG:LOG_DEBUG_2, "Got %d more bytes: %s", more, buffer); if(more > 0) { crm_realloc(output, len + more + 1); sprintf(output+len, "%s", buffer); len += more; } } while (more == (READ_MAX-1) || (more < 0 && errno == EINTR)); if(cmd->stdout) { close(cmd->stdout); cmd->stdout = 0; } while(rc != 0 && cmd->device_next) { int exec_rc = 0; stonith_device_t *dev = cmd->device_next->data; const char *victim = get_victim_name(dev, cmd->victim); log_operation(cmd, rc, pid, dev->id, output); cmd->device = dev->id; cmd->device_next = cmd->device_next->next; exec_rc = run_stonith_agent(dev->agent, dev->params, cmd->node_attrs, cmd->action, victim, &rc, NULL, cmd); if(exec_rc > 0) { goto done; } pid = exec_rc; } reply = stonith_construct_async_reply(cmd, output, data, rc); if(safe_str_eq(cmd->action, "metadata")) { /* Too verbose to log */ crm_free(output); output = NULL; } else if(crm_str_eq(cmd->action, "reboot", TRUE) || crm_str_eq(cmd->action, "poweroff", TRUE) || crm_str_eq(cmd->action, "poweron", TRUE) || crm_str_eq(cmd->action, "off", TRUE) || crm_str_eq(cmd->action, "on", TRUE)) { bcast = TRUE; } log_operation(cmd, rc, pid, NULL, output); crm_log_xml_debug_3(reply, "Reply"); if(bcast) { /* Send reply as T_STONITH_NOTIFY so everyone does notifications * Potentially limit to unsucessful operations to the originator? */ crm_xml_add(reply, F_STONITH_OPERATION, T_STONITH_NOTIFY); send_cluster_message(NULL, crm_msg_stonith_ng, reply, FALSE); } else if(cmd->origin) { send_cluster_message(cmd->origin, crm_msg_stonith_ng, reply, FALSE); } else { do_local_reply(reply, cmd->client, cmd->options & st_opt_sync_call, FALSE); } free_async_command(cmd); done: reset_proctrack_data(proc); crm_free(output); free_xml(reply); free_xml(data); } static gint sort_device_priority(gconstpointer a, gconstpointer b) { const stonith_device_t *dev_a = a; const stonith_device_t *dev_b = a; if(dev_a->priority > dev_b->priority) { return -1; } else if(dev_a->priority < dev_b->priority) { return 1; } return 0; } static int stonith_fence(xmlNode *msg) { int rc = 0; struct device_search_s search; stonith_device_t *device = NULL; async_command_t *cmd = create_async_command(msg, crm_element_value(msg, F_STONITH_ACTION)); xmlNode *dev = get_xpath_object("//@"F_STONITH_TARGET, msg, LOG_ERR); GHashTable *node_attrs = xml2list(dev); if(cmd == NULL) { return st_err_internal; } search.capable = NULL; search.host = crm_element_value(dev, F_STONITH_TARGET); crm_log_xml_info(msg, "Exec"); g_hash_table_foreach(device_list, search_devices, &search); crm_info("Found %d matching devices for '%s'", g_list_length(search.capable), search.host); if(g_list_length(search.capable) == 0) { free_async_command(cmd); return st_err_none_available; } /* Order based on priority */ search.capable = g_list_sort(search.capable, sort_device_priority); device = search.capable->data; cmd->device = device->id; if(g_list_length(search.capable) > 1) { cmd->device_list = search.capable; cmd->node_attrs = node_attrs; } return run_stonith_agent(device->agent, device->params, node_attrs, cmd->action, cmd->victim, &rc, NULL, cmd); } xmlNode *stonith_construct_reply(xmlNode *request, char *output, xmlNode *data, int rc) { int lpc = 0; xmlNode *reply = NULL; const char *name = NULL; const char *value = NULL; const char *names[] = { F_STONITH_OPERATION, F_STONITH_CALLID, F_STONITH_CLIENTID, F_STONITH_REMOTE, F_STONITH_CALLOPTS }; crm_debug_4("Creating a basic reply"); reply = create_xml_node(NULL, T_STONITH_REPLY); crm_xml_add(reply, "st_origin", __FUNCTION__); crm_xml_add(reply, F_TYPE, T_STONITH_NG); crm_xml_add(reply, "st_output", output); crm_xml_add_int(reply, F_STONITH_RC, rc); CRM_CHECK(request != NULL, crm_warn("Can't create a sane reply"); return reply); for(lpc = 0; lpc < DIMOF(names); lpc++) { name = names[lpc]; value = crm_element_value(request, name); crm_xml_add(reply, name, value); } if(data != NULL) { crm_debug_4("Attaching reply output"); add_message_xml(reply, F_STONITH_CALLDATA, data); } return reply; } xmlNode *stonith_construct_async_reply(async_command_t *cmd, char *output, xmlNode *data, int rc) { xmlNode *reply = NULL; crm_debug_4("Creating a basic reply"); reply = create_xml_node(NULL, T_STONITH_REPLY); crm_xml_add(reply, "st_origin", __FUNCTION__); crm_xml_add(reply, F_TYPE, T_STONITH_NG); crm_xml_add(reply, F_STONITH_OPERATION, cmd->op); crm_xml_add(reply, F_STONITH_REMOTE, cmd->remote); crm_xml_add(reply, F_STONITH_CLIENTID, cmd->client); crm_xml_add_int(reply, F_STONITH_CALLID, cmd->id); crm_xml_add_int(reply, F_STONITH_CALLOPTS, cmd->options); crm_xml_add_int(reply, F_STONITH_RC, rc); crm_xml_add(reply, "st_output", output); if(data != NULL) { crm_info("Attaching reply output"); add_message_xml(reply, F_STONITH_CALLDATA, data); } return reply; } void stonith_command(stonith_client_t *client, xmlNode *request, const char *remote) { int call_options = 0; int rc = st_err_generic; gboolean is_reply = FALSE; xmlNode *reply = NULL; xmlNode *data = NULL; char *output = NULL; const char *op = crm_element_value(request, F_STONITH_OPERATION); const char *client_id = crm_element_value(request, F_STONITH_CLIENTID); crm_element_value_int(request, F_STONITH_CALLOPTS, &call_options); if(get_xpath_object("//"T_STONITH_REPLY, request, LOG_DEBUG_3)) { is_reply = TRUE; } if(device_list == NULL) { device_list = g_hash_table_new_full( g_str_hash, g_str_equal, NULL, free_device); } crm_debug("Processing %s%s from %s", op, is_reply?" reply":"", client?client->name:remote); if(crm_str_eq(op, CRM_OP_REGISTER, TRUE)) { return; } else if(crm_str_eq(op, STONITH_OP_DEVICE_ADD, TRUE)) { rc = stonith_device_register(request); do_stonith_notify(call_options, op, rc, request, NULL); } else if(crm_str_eq(op, STONITH_OP_DEVICE_DEL, TRUE)) { rc = stonith_device_remove(request); do_stonith_notify(call_options, op, rc, request, NULL); } else if(crm_str_eq(op, STONITH_OP_CONFIRM, TRUE)) { async_command_t *cmd = create_async_command(request, crm_element_value(request, F_STONITH_ACTION)); xmlNode *reply = stonith_construct_async_reply(cmd, NULL, NULL, 0); crm_xml_add(reply, F_STONITH_OPERATION, T_STONITH_NOTIFY); crm_notice("Broadcasting manual fencing confirmation for node %s", cmd->victim); send_cluster_message(NULL, crm_msg_stonith_ng, reply, FALSE); free_async_command(cmd); free_xml(reply); } else if(crm_str_eq(op, STONITH_OP_EXEC, TRUE)) { rc = stonith_device_action(request, &output); } else if(is_reply && crm_str_eq(op, STONITH_OP_QUERY, TRUE)) { process_remote_stonith_query(request); return; } else if(crm_str_eq(op, STONITH_OP_QUERY, TRUE)) { create_remote_stonith_op(client_id, request, TRUE); /* Record it for the future notification */ rc = stonith_query(request, &data); } else if(is_reply && crm_str_eq(op, T_STONITH_NOTIFY, TRUE)) { process_remote_stonith_exec(request); return; } else if(crm_str_eq(op, T_STONITH_NOTIFY, TRUE)) { const char *flag_name = NULL; flag_name = crm_element_value(request, F_STONITH_NOTIFY_ACTIVATE); if(flag_name) { crm_debug("Setting %s callbacks for %s (%s): ON", flag_name, client->name, client->id); client->flags |= get_stonith_flag(flag_name); } flag_name = crm_element_value(request, F_STONITH_NOTIFY_DEACTIVATE); if(flag_name) { crm_debug("Setting %s callbacks for %s (%s): off", flag_name, client->name, client->id); client->flags |= get_stonith_flag(flag_name); } return; /* } else if(is_reply && crm_str_eq(op, STONITH_OP_FENCE, TRUE)) { */ /* process_remote_stonith_exec(request); */ /* return; */ } else if(is_reply == FALSE && crm_str_eq(op, STONITH_OP_FENCE, TRUE)) { if(remote) { rc = stonith_fence(request); } else if(call_options & st_opt_local_first) { rc = stonith_fence(request); if(rc < 0) { initiate_remote_stonith_op(client, request); } } else { initiate_remote_stonith_op(client, request); } return; } else { crm_err("Unknown %s%s from %s", op, is_reply?" reply":"", client?client->name:remote); crm_log_xml_warn(request, "UnknownOp"); } do_crm_log(rc>0?LOG_DEBUG:LOG_INFO,"Processed %s%s from %s: rc=%d", op, is_reply?" reply":"", client?client->name:remote, rc); if(is_reply) { /* Nothing */ } else if(remote) { reply = stonith_construct_reply(request, output, data, rc); send_cluster_message(remote, crm_msg_stonith_ng, reply, FALSE); free_xml(reply); } else if(rc <= 0 || crm_str_eq(op, STONITH_OP_QUERY, TRUE)) { reply = stonith_construct_reply(request, output, data, rc); do_local_reply(reply, client_id, call_options & st_opt_sync_call, remote!=NULL); free_xml(reply); } crm_free(output); free_xml(data); } diff --git a/include/crm/common/xml.h b/include/crm/common/xml.h index ee70149b3d..60b8296e01 100644 --- a/include/crm/common/xml.h +++ b/include/crm/common/xml.h @@ -1,313 +1,317 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef CRM_COMMON_XML__H #define CRM_COMMON_XML__H #include #include #include #include #include #include #include #include #include #include typedef xmlNode crm_data_t; /* Encryption costs a LOT, don't do it unless we're hitting message limits * * For now, use 256k as the lower size, which means we can have 4 big data fields * before we hit heartbeat's message limit * * The previous limit was 10k, compressing 184 of 1071 messages accounted for 23% * of the total CPU used by the cib */ #define CRM_BZ2_BLOCKS 4 #define CRM_BZ2_WORK 20 #define CRM_BZ2_THRESHOLD 128 * 1024 #define XML_PARANOIA_CHECKS 0 extern gboolean add_message_xml( xmlNode *msg, const char *field, xmlNode *xml); extern xmlNode *get_message_xml(xmlNode *msg, const char *field); extern GHashTable *xml2list(xmlNode *parent); #if CRM_DEPRECATED_SINCE_2_0_3 extern GHashTable *xml2list_202(xmlNode *parent); #endif extern void hash2nvpair(gpointer key, gpointer value, gpointer user_data); extern void hash2field(gpointer key, gpointer value, gpointer user_data); extern void hash2metafield(gpointer key, gpointer value, gpointer user_data); extern void hash2smartfield(gpointer key, gpointer value, gpointer user_data); /* * Replacement function for xmlCopyPropList which at the very least, * doesnt work the way *I* would expect it to. * * Copy all the attributes/properties from src into target. * * Not recursive, does not return anything. * */ extern void copy_in_properties(xmlNode *target, xmlNode *src); extern void expand_plus_plus(xmlNode* target, const char *name, const char *value); extern void fix_plus_plus_recursive(xmlNode* target); void free_xml_from_parent(xmlNode *parent, xmlNode *a_node); #define zap_xml_from_parent(parent, xml_obj) free_xml_from_parent(parent, xml_obj); xml_obj = NULL /* * Create a node named "name" as a child of "parent" * If parent is NULL, creates an unconnected node. * * Returns the created node * */ extern xmlNode *create_xml_node(xmlNode *parent, const char *name); /* * Make a copy of name and value and use the copied memory to create * an attribute for node. * * If node, name or value are NULL, nothing is done. * * If name or value are an empty string, nothing is done. * * Returns FALSE on failure and TRUE on success. * */ extern const char *crm_xml_add( xmlNode *node, const char *name, const char *value); extern const char *crm_xml_replace( xmlNode *node, const char *name, const char *value); extern const char *crm_xml_add_int( xmlNode* node, const char *name, int value); /* * Unlink the node and set its doc pointer to NULL so free_xml() * will act appropriately */ extern void unlink_xml_node(xmlNode *node); /* * */ extern void purge_diff_markers(xmlNode *a_node); /* * Returns a deep copy of src_node * */ extern xmlNode *copy_xml(xmlNode *src_node); /* * Add a copy of xml_node to new_parent */ extern xmlNode *add_node_copy( xmlNode *new_parent, xmlNode *xml_node); extern int add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child); /* * XML I/O Functions * * Whitespace between tags is discarded. */ extern xmlNode *filename2xml(const char *filename); extern xmlNode *stdin2xml(void); extern xmlNode *string2xml(const char *input); extern int write_xml_file( xmlNode *xml_node, const char *filename, gboolean compress); extern char *dump_xml_formatted(xmlNode *msg); extern char *dump_xml_unformatted(xmlNode *msg); /* * Diff related Functions */ extern xmlNode *diff_xml_object( xmlNode *left, xmlNode *right, gboolean suppress); extern void print_xml_diff(FILE *where, xmlNode *diff); extern void log_xml_diff(unsigned int log_level, xmlNode *diff, const char *function); extern gboolean apply_xml_diff( xmlNode *old, xmlNode *diff, xmlNode **new); /* * Searching & Modifying */ extern xmlNode *find_xml_node( xmlNode *cib, const char * node_path, gboolean must_find); extern xmlNode *find_entity( xmlNode *parent, const char *node_name, const char *id); extern void xml_remove_prop(xmlNode *obj, const char *name); extern gboolean replace_xml_child( xmlNode *parent, xmlNode *child, xmlNode *update, gboolean delete_only); extern gboolean update_xml_child(xmlNode *child, xmlNode *to_update); extern int find_xml_children( xmlNode **children, xmlNode *root, const char *tag, const char *field, const char *value, gboolean search_matches); extern int crm_element_value_int(xmlNode *data, const char *name, int *dest); extern char *crm_element_value_copy(xmlNode *data, const char *name); extern const char *crm_element_value_const(const xmlNode *data, const char *name); extern xmlNode *get_xpath_object(const char *xpath, xmlNode *xml_obj, int error_level); extern xmlNode *get_xpath_object_relative(const char *xpath, xmlNode *xml_obj, int error_level); #define crm_element_name(xml) (xml)?(const char *)(xml)->name:NULL extern const char *crm_element_value(xmlNode *data, const char *name); extern void xml_validate(const xmlNode *root); extern gboolean xml_has_children(const xmlNode *root); /* For ABI compatability with version < 1.1.4 */ extern char *calculate_xml_digest(xmlNode *local_cib, gboolean sort, gboolean do_filter); extern char *calculate_on_disk_digest(xmlNode *local_cib); extern char *calculate_operation_digest(xmlNode *local_cib, const char *version); extern char *calculate_xml_versioned_digest(xmlNode *input, gboolean sort, gboolean do_filter, const char *version); extern gboolean validate_xml(xmlNode *xml_blob, const char *validation, gboolean to_logs); extern gboolean validate_xml_verbose(xmlNode *xml_blob); extern int update_validation(xmlNode **xml_blob, int *best, gboolean transform, gboolean to_logs); extern int get_schema_version(const char *name); extern const char *get_schema_name(int version); extern void crm_xml_cleanup(void); #if XML_PARANOIA_CHECKS # define crm_validate_data(obj) xml_validate(obj) #else # define crm_validate_data(obj) CRM_LOG_ASSERT(obj != NULL) #endif +/* These two child iterator macros are no longer to be used + * They exist for compatability reasons and will be removed in a + * future release + */ # define xml_child_iter(parent, child, code) do { \ if(parent != NULL) { \ xmlNode *child = NULL; \ xmlNode *__crm_xml_iter = parent->children; \ while(__crm_xml_iter != NULL) { \ child = __crm_xml_iter; \ __crm_xml_iter = __crm_xml_iter->next; \ if(child->type == XML_ELEMENT_NODE) { \ code; \ } \ } \ } \ } while(0) # define xml_child_iter_filter(parent, child, filter, code) do { \ if(parent != NULL) { \ xmlNode *child = NULL; \ xmlNode *__crm_xml_iter = parent->children; \ while(__crm_xml_iter != NULL) { \ child = __crm_xml_iter; \ __crm_xml_iter = __crm_xml_iter->next; \ if(child->type == XML_ELEMENT_NODE) { \ if(filter == NULL \ || crm_str_eq(filter, (const char *)child->name, TRUE)) { \ code; \ } \ } \ } \ } \ } while(0) # define xml_prop_iter(parent, prop_name, prop_value, code) do { \ if(parent != NULL) { \ xmlAttrPtr prop_iter = parent->properties; \ const char *prop_name = NULL; \ const char *prop_value = NULL; \ while(prop_iter != NULL) { \ prop_name = (const char *)prop_iter->name; \ prop_value = crm_element_value(parent, prop_name); \ prop_iter = prop_iter->next; \ if(prop_name) { \ code; \ } \ } \ } \ } while(0) # define xml_prop_name_iter(parent, prop_name, code) do { \ if(parent != NULL) { \ xmlAttrPtr prop_iter = parent->properties; \ const char *prop_name = NULL; \ while(prop_iter != NULL) { \ prop_name = (const char *)prop_iter->name; \ prop_iter = prop_iter->next; \ if(prop_name) { \ code; \ } \ } \ } \ } while(0) # define free_xml(a_node) do { \ if((a_node) != NULL) { \ xmlNode *a_doc_top = NULL; \ xmlDoc *a_doc = (a_node)->doc; \ if (a_doc != NULL) { \ a_doc_top = xmlDocGetRootElement(a_doc); \ } \ if(a_doc != NULL && a_doc_top == (a_node)) { \ xmlFreeDoc(a_doc); \ \ } else { \ /* make sure the node is unlinked first */ \ xmlUnlinkNode(a_node); \ xmlFreeNode(a_node); \ } \ } \ } while(0) extern xmlNode *first_named_child(xmlNode *parent, const char *name); extern xmlNode *convert_ipc_message(IPC_Message *msg, const char *field); extern xmlNode *convert_ha_message(xmlNode *parent, HA_Message *msg, const char *field); extern HA_Message *convert_xml_message(xmlNode *msg); extern xmlNode *sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive); extern xmlXPathObjectPtr xpath_search(xmlNode *xml_top, const char *path); extern gboolean cli_config_update(xmlNode **xml, int *best_version, gboolean to_logs); extern xmlNode *expand_idref(xmlNode *input, xmlNode *top); extern xmlNode *getXpathResult(xmlXPathObjectPtr xpathObj, int index); #endif diff --git a/include/crm/crm.h b/include/crm/crm.h index ae3ae478ef..ef547bf42d 100644 --- a/include/crm/crm.h +++ b/include/crm/crm.h @@ -1,443 +1,455 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef CRM__H #define CRM__H #include #include #include #undef MIN #undef MAX #include #include #include extern int log_data_element( int log_level, const char *file, const char *function, int line, const char *prefix, xmlNode *data, int depth, gboolean formatted); #define CRM_FEATURE_SET "3.0.5" #define MINIMUM_SCHEMA_VERSION "pacemaker-1.0" #define LATEST_SCHEMA_VERSION "pacemaker-"CRM_DTD_VERSION #define EOS '\0' #define DIMOF(a) ((int) (sizeof(a)/sizeof(a[0])) ) #ifndef __GNUC__ # define __builtin_expect(expr, result) (expr) #endif /* Some handy macros used by the Linux kernel */ #define __likely(expr) __builtin_expect(expr, 1) #define __unlikely(expr) __builtin_expect(expr, 0) #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_ASSERT(expr) do { \ if(__unlikely((expr) == FALSE)) { \ crm_abort(__FILE__, __PRETTY_FUNCTION__, __LINE__, #expr, TRUE, FALSE); \ } \ } while(0) #define CRM_LOG_ASSERT(expr) do { \ if(__unlikely((expr) == FALSE)) { \ crm_abort(__FILE__, __PRETTY_FUNCTION__, __LINE__, #expr, FALSE, TRUE); \ } \ } while(0) extern const char *crm_system_name; /* Clean these up at some point, some probably should be runtime options */ #define SOCKET_LEN 1024 #define APPNAME_LEN 256 #define MAX_IPC_FAIL 5 #define MAX_IPC_DELAY 120 #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" #define CRM_SYSTEM_STONITHD "stonithd" /* 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 CRM_OP_CLEAR_FAILCOUNT "clear_failcount" #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_JOINSTATE_NACK "banned" #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; } \ - } \ - } /* LOG_DEBUG = 7, make LOG_TRACE ::= -VVVVV */ #define LOG_TRACE 12 #define LOG_DEBUG_2 LOG_TRACE #define LOG_DEBUG_3 LOG_TRACE #define LOG_DEBUG_4 LOG_TRACE #define LOG_DEBUG_5 LOG_TRACE #define LOG_DEBUG_6 LOG_TRACE #define LOG_MSG LOG_TRACE #if SUPPORT_TRACING struct _pcmk_ddebug_query { const char *files; const char *formats; const char *functions; unsigned long long total; unsigned long long matches; }; /* * An instance of this structure is created in a special * ELF section at every dynamic debug callsite. At runtime, * the special section is treated as an array of these. */ struct _pcmk_ddebug { /* * These fields are used to drive the user interface * for selecting and displaying debug callsites. */ const char *function; const char *filename; const char *format; unsigned int lineno:24; /* * The bump field will add to the level at the callsite. * The value here are changed dynamically when the user * writes commands to FIXME ;-) */ int bump; } __attribute__((aligned(8))); /* will be assigned by ld linker magic */ extern struct _pcmk_ddebug __start___verbose[]; extern struct _pcmk_ddebug __stop___verbose[]; # define CRM_TRACE_INIT_DATA(name) \ void name(void); \ void name(void) { CRM_ASSERT(__start___verbose != __stop___verbose); } \ void __attribute__ ((constructor)) name(void); #define CRM_CHECK(expr, failure_action) do { \ static struct _pcmk_ddebug descriptor \ __attribute__((section("__verbose"), aligned(8))) = \ { __func__, __FILE__, #expr, __LINE__, LOG_TRACE}; \ \ if(__unlikely((expr) == FALSE)) { \ crm_abort(__FILE__, __PRETTY_FUNCTION__, __LINE__, #expr, \ descriptor.bump != LOG_TRACE, TRUE); \ failure_action; \ } \ } while(0) /* * 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 { \ static struct _pcmk_ddebug descriptor \ __attribute__((section("__verbose"), aligned(8))) = \ { __func__, __FILE__, fmt, __LINE__, LOG_TRACE}; \ \ if(__likely((level) <= crm_log_level)) { \ cl_log((level), "%s: " fmt, __PRETTY_FUNCTION__ , ##args); \ \ } else if(__unlikely(descriptor.bump != LOG_TRACE)) { \ cl_log(descriptor.bump, "TRACE: %s: %s:%d " fmt, __PRETTY_FUNCTION__ , __FILE__, __LINE__, ##args); \ } \ } while(0) # define do_crm_log_unlikely(level, fmt, args...) do { \ static struct _pcmk_ddebug descriptor \ __attribute__((section("__verbose"), aligned(8))) = \ { __func__, __FILE__, fmt, __LINE__, LOG_TRACE }; \ \ if(__unlikely((level) <= crm_log_level)) { \ cl_log((level), "%s: " fmt, __PRETTY_FUNCTION__ , ##args); \ \ } else if(__unlikely(descriptor.bump != LOG_TRACE)) { \ cl_log(descriptor.bump, "TRACE: %s: %s:%d " fmt, __PRETTY_FUNCTION__ , __FILE__, __LINE__, ##args); \ } \ } while(0) # define do_crm_log_xml(level, text, xml) do { \ static struct _pcmk_ddebug descriptor \ __attribute__((section("__verbose"), aligned(8))) = \ { __func__, __FILE__, __PRETTY_FUNCTION__, __LINE__, LOG_TRACE }; \ \ if(xml == NULL) { \ } else if(__likely((level) <= crm_log_level)) { \ log_data_element(level, __FILE__, __PRETTY_FUNCTION__, 0, text, xml, 0, TRUE); \ \ } else if(__unlikely(descriptor.bump != LOG_TRACE)) { \ log_data_element(descriptor.bump, __FILE__, __PRETTY_FUNCTION__, __LINE__, text, xml, 0, TRUE); \ } \ } while(0) # define do_crm_log_alias(level, file, function, line, fmt, args...) do { \ if(line) { \ cl_log(level, "TRACE: %s %s:%d "fmt, function, file, line, ##args); \ } else { \ cl_log(level, "%s "fmt, function, ##args); \ } \ } while(0) #else # define CRM_TRACE_INIT_DATA(name) #define CRM_CHECK(expr, failure_action) do { \ if(__unlikely((expr) == FALSE)) { \ crm_abort(__FILE__,__PRETTY_FUNCTION__,__LINE__, #expr, FALSE, TRUE); \ failure_action; \ } \ } while(0) # define do_crm_log(level, fmt, args...) do { \ if(__likely((level) <= crm_log_level)) { \ cl_log((level), "%s: " fmt, __PRETTY_FUNCTION__ , ##args); \ } \ } while(0) # define do_crm_log_unlikely(level, fmt, args...) do { \ if(__unlikely((level) <= crm_log_level)) { \ cl_log((level), "%s: " fmt, __PRETTY_FUNCTION__ , ##args); \ } \ } while(0) # define do_crm_log_xml(level, text, xml) do { \ if(xml == NULL) { \ } else if(__unlikely((level) <= crm_log_level)) { \ log_data_element(level, __FILE__, __PRETTY_FUNCTION__, 0, text, xml, 0, TRUE); \ } \ } while(0) # define do_crm_log_alias(level, file, function, line, fmt, args...) do { \ cl_log(level, "%s"fmt, function, ##args); \ } while(0) #endif #define do_crm_log_always(level, fmt, args...) cl_log(level, "%s: " fmt, __PRETTY_FUNCTION__ , ##args) #define crm_crit(fmt, args...) do_crm_log_always(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_unlikely(LOG_DEBUG, fmt , ##args) #define crm_trace(fmt, args...) do_crm_log_unlikely(LOG_TRACE, fmt , ##args) #define crm_debug_2 crm_trace #define crm_debug_3 crm_trace #define crm_debug_4 crm_trace #define crm_debug_5 crm_trace #define crm_debug_6 crm_trace #define crm_perror(level, fmt, args...) do { \ const char *err = strerror(errno); \ fprintf(stderr, fmt ": %s (%d)\n", ##args, err, errno); \ do_crm_log(level, fmt ": %s (%d)", ##args, err, errno); \ } while(0) #include #define crm_log_xml_crit(xml, text) do_crm_log_xml(LOG_CRIT, text, xml) #define crm_log_xml_err(xml, text) do_crm_log_xml(LOG_ERR, text, xml) #define crm_log_xml_warn(xml, text) do_crm_log_xml(LOG_WARNING, text, xml) #define crm_log_xml_notice(xml, text) do_crm_log_xml(LOG_NOTICE, text, xml) #define crm_log_xml_info(xml, text) do_crm_log_xml(LOG_INFO, text, xml) #define crm_log_xml_debug(xml, text) do_crm_log_xml(LOG_DEBUG, text, xml) #define crm_log_xml_trace(xml, text) do_crm_log_xml(LOG_TRACE, text, xml) #define crm_log_xml do_crm_log_xml #define crm_log_xml_debug_2 crm_log_xml_trace #define crm_log_xml_debug_3 crm_log_xml_trace #define crm_log_xml_debug_4 crm_log_xml_trace #define crm_log_xml_debug_5 crm_log_xml_trace #define crm_str(x) (const char*)(x?x:"") #define crm_malloc0(malloc_obj, length) do { \ malloc_obj = 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_malloc(malloc_obj, length) do { \ malloc_obj = malloc(length); \ if(malloc_obj == NULL) { \ crm_err("Failed allocation of %lu bytes", (unsigned long)length); \ CRM_ASSERT(malloc_obj != NULL); \ } \ } while(0) #define crm_realloc(realloc_obj, length) do { \ realloc_obj = realloc(realloc_obj, length); \ CRM_ASSERT(realloc_obj != NULL); \ } while(0) #define crm_free(free_obj) do { free(free_obj); free_obj=NULL; } while(0) #define crm_msg_del(msg) do { if(msg != NULL) { ha_msg_del(msg); msg = NULL; } } while(0) #define crm_strdup(str) crm_strdup_fn(str, __FILE__, __PRETTY_FUNCTION__, __LINE__) extern void update_all_trace_data(void); +static inline void slist_basic_destroy(GListPtr list) +{ + GListPtr gIter = NULL; + for(gIter = list; gIter != NULL; gIter = gIter->next) { + free(gIter->data); + } + g_list_free(list); +} + +/* These two macros are no longer to be used + * They exist for compatability reasons and will be removed in a + * future release + */ +#define slist_destroy(child_type, child, parent, a) do { \ + 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); \ + } while(0) + +#define slist_iter(child, child_type, parent, counter, a) do { \ + 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; } \ + } \ + } while(0) + #endif diff --git a/lib/cib/cib_attrs.c b/lib/cib/cib_attrs.c index e6b3993bc8..9c4f944295 100644 --- a/lib/cib/cib_attrs.c +++ b/lib/cib/cib_attrs.c @@ -1,493 +1,496 @@ - /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define attr_msg(level, fmt, args...) do { \ if(to_console) { \ printf(fmt"\n", ##args); \ } else { \ do_crm_log(level, fmt , ##args); \ } \ -} while(0) + } while(0) extern enum cib_errors find_nvpair_attr( cib_t *the_cib, const char *attr, const char *section, const char *node_uuid, const char *attr_set_type, const char *set_name, const char *attr_id, const char *attr_name, gboolean to_console, char **value) { int offset = 0; static int xpath_max = 1024; enum cib_errors rc = cib_ok; char *xpath_string = NULL; xmlNode *xml_search = NULL; const char *set_type = NULL; if (attr_set_type) { set_type = attr_set_type; } else { set_type = XML_TAG_ATTR_SETS; } CRM_ASSERT(value != NULL); *value = NULL; if(safe_str_eq(section, XML_CIB_TAG_CRMCONFIG)) { node_uuid = NULL; set_type = XML_CIB_TAG_PROPSET; } else if(safe_str_eq(section, XML_CIB_TAG_OPCONFIG) || safe_str_eq(section, XML_CIB_TAG_RSCCONFIG)) { node_uuid = NULL; set_type = XML_TAG_META_SETS; } else if(node_uuid == NULL) { return cib_missing_data; } crm_malloc0(xpath_string, xpath_max); offset += snprintf(xpath_string + offset, xpath_max - offset, "%s", get_object_path(section)); if(node_uuid) { const char *node_type = XML_CIB_TAG_NODE; if(safe_str_eq(section, XML_CIB_TAG_STATUS)) { node_type = XML_CIB_TAG_STATE; set_type = XML_TAG_TRANSIENT_NODEATTRS; } offset += snprintf(xpath_string + offset, xpath_max - offset, "//%s[@id='%s']", node_type, node_uuid); } if(set_name) { offset += snprintf(xpath_string + offset, xpath_max - offset, "//%s[@id='%s']", set_type, set_name); } else { offset += snprintf(xpath_string + offset, xpath_max - offset, "//%s", set_type); } offset += snprintf(xpath_string + offset, xpath_max - offset, "//nvpair["); if(attr_id) { offset += snprintf(xpath_string + offset, xpath_max - offset, "@id='%s'", attr_id); } if(attr_name) { if(attr_id) { offset += snprintf(xpath_string + offset, xpath_max - offset, " and "); } offset += snprintf(xpath_string + offset, xpath_max - offset, "@name='%s'", attr_name); } offset += snprintf(xpath_string + offset, xpath_max - offset, "]"); rc = the_cib->cmds->query( the_cib, xpath_string, &xml_search, cib_sync_call|cib_scope_local|cib_xpath); if(rc != cib_ok) { do_crm_log(LOG_DEBUG_2, "Query failed for attribute %s (section=%s, node=%s, set=%s, xpath=%s): %s", - attr_name, section, crm_str(node_uuid), crm_str(set_name), xpath_string, - cib_error2string(rc)); + attr_name, section, crm_str(node_uuid), crm_str(set_name), xpath_string, + cib_error2string(rc)); goto done; } crm_log_xml_debug(xml_search, "Match"); if(xml_has_children(xml_search)) { + xmlNode *child = NULL; rc = cib_missing_data; attr_msg(LOG_WARNING, "Multiple attributes match name=%s", attr_name); - xml_child_iter(xml_search, child, - attr_msg(LOG_INFO, " Value: %s \t(id=%s)", - crm_element_value(child, XML_NVPAIR_ATTR_VALUE), ID(child)); - ); + for(child = xml_search; child != NULL; child = child->next) { + attr_msg(LOG_INFO, " Value: %s \t(id=%s)", + crm_element_value(child, XML_NVPAIR_ATTR_VALUE), ID(child)); + } } else { const char *tmp = crm_element_value(xml_search, attr); if(tmp) { *value = crm_strdup(tmp); } } done: crm_free(xpath_string); free_xml(xml_search); return rc; } enum cib_errors update_attr(cib_t *the_cib, int call_options, const char *section, const char *node_uuid, const char *set_type, const char *set_name, const char *attr_id, const char *attr_name, const char *attr_value, gboolean to_console) { - const char *tag = NULL; - enum cib_errors rc = cib_ok; - xmlNode *xml_top = NULL; - xmlNode *xml_obj = NULL; - - char *local_attr_id = NULL; - char *local_set_name = NULL; - gboolean use_attributes_tag = FALSE; + const char *tag = NULL; + enum cib_errors rc = cib_ok; + xmlNode *xml_top = NULL; + xmlNode *xml_obj = NULL; + + char *local_attr_id = NULL; + char *local_set_name = NULL; + gboolean use_attributes_tag = FALSE; - CRM_CHECK(section != NULL, return cib_missing); - CRM_CHECK(attr_value != NULL, return cib_missing); - CRM_CHECK(attr_name != NULL || attr_id != NULL, return cib_missing); + CRM_CHECK(section != NULL, return cib_missing); + CRM_CHECK(attr_value != NULL, return cib_missing); + CRM_CHECK(attr_name != NULL || attr_id != NULL, return cib_missing); - rc = find_nvpair_attr(the_cib, XML_ATTR_ID, section, node_uuid, set_type, set_name, attr_id, attr_name, FALSE, &local_attr_id); - if(rc == cib_ok) { - attr_id = local_attr_id; - goto do_modify; + rc = find_nvpair_attr(the_cib, XML_ATTR_ID, section, node_uuid, set_type, set_name, attr_id, attr_name, FALSE, &local_attr_id); + if(rc == cib_ok) { + attr_id = local_attr_id; + goto do_modify; - } else if(rc != cib_NOTEXISTS) { - return rc; + } else if(rc != cib_NOTEXISTS) { + return rc; /* } else if(attr_id == NULL) { */ /* return cib_missing; */ + } else { + const char *value = NULL; + xmlNode *cib_top = NULL; + rc = the_cib->cmds->query( + the_cib, "/cib", &cib_top, cib_sync_call|cib_scope_local|cib_xpath|cib_no_children); + + value = crm_element_value(cib_top, "ignore_dtd"); + if(value != NULL) { + use_attributes_tag = TRUE; + } else { - const char *value = NULL; - xmlNode *cib_top = NULL; - rc = the_cib->cmds->query( - the_cib, "/cib", &cib_top, cib_sync_call|cib_scope_local|cib_xpath|cib_no_children); - - value = crm_element_value(cib_top, "ignore_dtd"); - if(value != NULL) { + value = crm_element_value(cib_top, XML_ATTR_VALIDATION); + if(value && strstr(value, "-0.6")) { use_attributes_tag = TRUE; - - } else { - value = crm_element_value(cib_top, XML_ATTR_VALIDATION); - if(value && strstr(value, "-0.6")) { - use_attributes_tag = TRUE; - } } - free_xml(cib_top); + } + free_xml(cib_top); - if(safe_str_eq(section, XML_CIB_TAG_NODES)) { - tag = XML_CIB_TAG_NODE; - if(node_uuid == NULL) { - return cib_missing; - } - - } else if(safe_str_eq(section, XML_CIB_TAG_STATUS)) { - tag = XML_TAG_TRANSIENT_NODEATTRS; - if(node_uuid == NULL) { - return cib_missing; - } + if(safe_str_eq(section, XML_CIB_TAG_NODES)) { + tag = XML_CIB_TAG_NODE; + if(node_uuid == NULL) { + return cib_missing; + } - xml_obj = create_xml_node(xml_obj, XML_CIB_TAG_STATE); - crm_xml_add(xml_obj, XML_ATTR_ID, node_uuid); - if(xml_top == NULL) { - xml_top = xml_obj; - } + } else if(safe_str_eq(section, XML_CIB_TAG_STATUS)) { + tag = XML_TAG_TRANSIENT_NODEATTRS; + if(node_uuid == NULL) { + return cib_missing; + } - } else { - tag = section; - node_uuid = NULL; + xml_obj = create_xml_node(xml_obj, XML_CIB_TAG_STATE); + crm_xml_add(xml_obj, XML_ATTR_ID, node_uuid); + if(xml_top == NULL) { + xml_top = xml_obj; } - if(set_name == NULL) { - if(safe_str_eq(section, XML_CIB_TAG_CRMCONFIG)) { - local_set_name = crm_strdup(CIB_OPTIONS_FIRST); + } else { + tag = section; + node_uuid = NULL; + } + + if(set_name == NULL) { + if(safe_str_eq(section, XML_CIB_TAG_CRMCONFIG)) { + local_set_name = crm_strdup(CIB_OPTIONS_FIRST); - } else if(node_uuid) { - local_set_name = crm_concat(section, node_uuid, '-'); + } else if(node_uuid) { + local_set_name = crm_concat(section, node_uuid, '-'); - if(set_type) { - char *tmp_set_name = local_set_name; - local_set_name = crm_concat(tmp_set_name, set_type, '-'); - crm_free(tmp_set_name); - } - } else { - local_set_name = crm_concat(section, "options", '-'); + if(set_type) { + char *tmp_set_name = local_set_name; + local_set_name = crm_concat(tmp_set_name, set_type, '-'); + crm_free(tmp_set_name); } - set_name = local_set_name; + } else { + local_set_name = crm_concat(section, "options", '-'); } + set_name = local_set_name; + } - if(attr_id == NULL) { - local_attr_id = crm_concat(set_name, attr_name, '-'); - attr_id = local_attr_id; + if(attr_id == NULL) { + local_attr_id = crm_concat(set_name, attr_name, '-'); + attr_id = local_attr_id; - } else if(attr_name == NULL) { - attr_name = attr_id; - } + } else if(attr_name == NULL) { + attr_name = attr_id; + } - crm_debug_2("Creating %s/%s", section, tag); - if(tag != NULL) { - xml_obj = create_xml_node(xml_obj, tag); - crm_xml_add(xml_obj, XML_ATTR_ID, node_uuid); - if(xml_top == NULL) { - xml_top = xml_obj; - } + crm_debug_2("Creating %s/%s", section, tag); + if(tag != NULL) { + xml_obj = create_xml_node(xml_obj, tag); + crm_xml_add(xml_obj, XML_ATTR_ID, node_uuid); + if(xml_top == NULL) { + xml_top = xml_obj; } + } - if(node_uuid == NULL) { - if(safe_str_eq(section, XML_CIB_TAG_CRMCONFIG)) { - xml_obj = create_xml_node(xml_obj, XML_CIB_TAG_PROPSET); - } else { - xml_obj = create_xml_node(xml_obj, XML_TAG_META_SETS); - } - - } else if(set_type) { - xml_obj = create_xml_node(xml_obj, set_type); - + if(node_uuid == NULL) { + if(safe_str_eq(section, XML_CIB_TAG_CRMCONFIG)) { + xml_obj = create_xml_node(xml_obj, XML_CIB_TAG_PROPSET); } else { - xml_obj = create_xml_node(xml_obj, XML_TAG_ATTR_SETS); + xml_obj = create_xml_node(xml_obj, XML_TAG_META_SETS); } - crm_xml_add(xml_obj, XML_ATTR_ID, set_name); - if(xml_top == NULL) { - xml_top = xml_obj; - } + } else if(set_type) { + xml_obj = create_xml_node(xml_obj, set_type); + + } else { + xml_obj = create_xml_node(xml_obj, XML_TAG_ATTR_SETS); + } + crm_xml_add(xml_obj, XML_ATTR_ID, set_name); + + if(xml_top == NULL) { + xml_top = xml_obj; + } - if(use_attributes_tag) { - xml_obj = create_xml_node(xml_obj, XML_TAG_ATTRS); - } + if(use_attributes_tag) { + xml_obj = create_xml_node(xml_obj, XML_TAG_ATTRS); } + } do_modify: - xml_obj = create_xml_node(xml_obj, XML_CIB_TAG_NVPAIR); - if(xml_top == NULL) { - xml_top = xml_obj; - } + xml_obj = create_xml_node(xml_obj, XML_CIB_TAG_NVPAIR); + if(xml_top == NULL) { + xml_top = xml_obj; + } - crm_xml_add(xml_obj, XML_ATTR_ID, attr_id); - crm_xml_add(xml_obj, XML_NVPAIR_ATTR_NAME, attr_name); - crm_xml_add(xml_obj, XML_NVPAIR_ATTR_VALUE, attr_value); + crm_xml_add(xml_obj, XML_ATTR_ID, attr_id); + crm_xml_add(xml_obj, XML_NVPAIR_ATTR_NAME, attr_name); + crm_xml_add(xml_obj, XML_NVPAIR_ATTR_VALUE, attr_value); - crm_log_xml_debug_2(xml_top, "update_attr"); - rc = the_cib->cmds->modify( - the_cib, section, xml_top, call_options|cib_quorum_override); - - if(rc < cib_ok) { - attr_msg(LOG_ERR, "Error setting %s=%s (section=%s, set=%s): %s", - attr_name, attr_value, section, crm_str(set_name), - cib_error2string(rc)); - crm_log_xml_info(xml_top, "Update"); - } + crm_log_xml_debug_2(xml_top, "update_attr"); + rc = the_cib->cmds->modify( + the_cib, section, xml_top, call_options|cib_quorum_override); + + if(rc < cib_ok) { + attr_msg(LOG_ERR, "Error setting %s=%s (section=%s, set=%s): %s", + attr_name, attr_value, section, crm_str(set_name), + cib_error2string(rc)); + crm_log_xml_info(xml_top, "Update"); + } - crm_free(local_set_name); - crm_free(local_attr_id); - free_xml(xml_top); + crm_free(local_set_name); + crm_free(local_attr_id); + free_xml(xml_top); - return rc; + return rc; } enum cib_errors read_attr(cib_t *the_cib, const char *section, const char *node_uuid, const char *set_type, const char *set_name, const char *attr_id, const char *attr_name, char **attr_value, gboolean to_console) { - enum cib_errors rc = cib_ok; + enum cib_errors rc = cib_ok; - CRM_ASSERT(attr_value != NULL); - CRM_CHECK(section != NULL, return cib_missing); - CRM_CHECK(attr_name != NULL || attr_id != NULL, return cib_missing); + CRM_ASSERT(attr_value != NULL); + CRM_CHECK(section != NULL, return cib_missing); + CRM_CHECK(attr_name != NULL || attr_id != NULL, return cib_missing); - *attr_value = NULL; + *attr_value = NULL; - rc = find_nvpair_attr(the_cib, XML_NVPAIR_ATTR_VALUE, section, node_uuid, set_type, set_name, attr_id, attr_name, to_console, attr_value); - if(rc != cib_ok) { - do_crm_log(LOG_DEBUG_2, "Query failed for attribute %s (section=%s, node=%s, set=%s): %s", - attr_name, section, crm_str(set_name), crm_str(node_uuid), - cib_error2string(rc)); - } - return rc; + rc = find_nvpair_attr(the_cib, XML_NVPAIR_ATTR_VALUE, section, node_uuid, set_type, set_name, attr_id, attr_name, to_console, attr_value); + if(rc != cib_ok) { + do_crm_log(LOG_DEBUG_2, "Query failed for attribute %s (section=%s, node=%s, set=%s): %s", + attr_name, section, crm_str(set_name), crm_str(node_uuid), + cib_error2string(rc)); + } + return rc; } enum cib_errors delete_attr(cib_t *the_cib, int options, const char *section, const char *node_uuid, const char *set_type, const char *set_name, const char *attr_id, const char *attr_name, const char *attr_value, gboolean to_console) { - enum cib_errors rc = cib_ok; - xmlNode *xml_obj = NULL; - char *local_attr_id = NULL; + enum cib_errors rc = cib_ok; + xmlNode *xml_obj = NULL; + char *local_attr_id = NULL; - CRM_CHECK(section != NULL, return cib_missing); - CRM_CHECK(attr_name != NULL || attr_id != NULL, return cib_missing); + CRM_CHECK(section != NULL, return cib_missing); + CRM_CHECK(attr_name != NULL || attr_id != NULL, return cib_missing); - if(attr_id == NULL) { - rc = find_nvpair_attr(the_cib, XML_ATTR_ID, section, node_uuid, set_type, set_name, attr_id, attr_name, to_console, &local_attr_id); - if(rc != cib_ok) { - return rc; - } - attr_id = local_attr_id; + if(attr_id == NULL) { + rc = find_nvpair_attr(the_cib, XML_ATTR_ID, section, node_uuid, set_type, set_name, attr_id, attr_name, to_console, &local_attr_id); + if(rc != cib_ok) { + return rc; } + attr_id = local_attr_id; + } - xml_obj = create_xml_node(NULL, XML_CIB_TAG_NVPAIR); - crm_xml_add(xml_obj, XML_ATTR_ID, attr_id); - crm_xml_add(xml_obj, XML_NVPAIR_ATTR_NAME, attr_name); - crm_xml_add(xml_obj, XML_NVPAIR_ATTR_VALUE, attr_value); + xml_obj = create_xml_node(NULL, XML_CIB_TAG_NVPAIR); + crm_xml_add(xml_obj, XML_ATTR_ID, attr_id); + crm_xml_add(xml_obj, XML_NVPAIR_ATTR_NAME, attr_name); + crm_xml_add(xml_obj, XML_NVPAIR_ATTR_VALUE, attr_value); - rc = the_cib->cmds->delete( - the_cib, section, xml_obj, options|cib_quorum_override); - - if(rc == cib_ok) { - attr_msg(LOG_DEBUG, "Deleted %s %s: id=%s%s%s%s%s\n", - section, node_uuid?"attribute":"option", local_attr_id, - set_name?" set=":"", set_name?set_name:"", - attr_name?" name=":"", attr_name?attr_name:""); - } + rc = the_cib->cmds->delete( + the_cib, section, xml_obj, options|cib_quorum_override); + + if(rc == cib_ok) { + attr_msg(LOG_DEBUG, "Deleted %s %s: id=%s%s%s%s%s\n", + section, node_uuid?"attribute":"option", local_attr_id, + set_name?" set=":"", set_name?set_name:"", + attr_name?" name=":"", attr_name?attr_name:""); + } - crm_free(local_attr_id); - free_xml(xml_obj); - return rc; + crm_free(local_attr_id); + free_xml(xml_obj); + return rc; } enum cib_errors query_node_uuid(cib_t *the_cib, const char *uname, char **uuid) { - enum cib_errors rc = cib_ok; - xmlNode *xml_obj = NULL; - xmlNode *fragment = NULL; - const char *child_name = NULL; + enum cib_errors rc = cib_ok; + xmlNode *a_child = NULL; + xmlNode *xml_obj = NULL; + xmlNode *fragment = NULL; + const char *child_name = NULL; - CRM_ASSERT(uname != NULL); - CRM_ASSERT(uuid != NULL); + CRM_ASSERT(uname != NULL); + CRM_ASSERT(uuid != NULL); - rc = the_cib->cmds->query(the_cib, XML_CIB_TAG_NODES, &fragment, - cib_sync_call|cib_scope_local); - if(rc != cib_ok) { - return rc; - } + rc = the_cib->cmds->query(the_cib, XML_CIB_TAG_NODES, &fragment, + cib_sync_call|cib_scope_local); + if(rc != cib_ok) { + return rc; + } - xml_obj = fragment; - CRM_CHECK(safe_str_eq(crm_element_name(xml_obj), XML_CIB_TAG_NODES), - return cib_output_data); - CRM_ASSERT(xml_obj != NULL); - crm_log_xml_debug(xml_obj, "Result section"); + xml_obj = fragment; + CRM_CHECK(safe_str_eq(crm_element_name(xml_obj), XML_CIB_TAG_NODES), + return cib_output_data); + CRM_ASSERT(xml_obj != NULL); + crm_log_xml_debug(xml_obj, "Result section"); - rc = cib_NOTEXISTS; - *uuid = NULL; + rc = cib_NOTEXISTS; + *uuid = NULL; - xml_child_iter_filter( - xml_obj, a_child, XML_CIB_TAG_NODE, - child_name = crm_element_value(a_child, XML_ATTR_UNAME); - - if(safe_str_eq(uname, child_name)) { - child_name = ID(a_child); - if(child_name != NULL) { - *uuid = crm_strdup(child_name); - rc = cib_ok; - } - break; + for(a_child = xml_obj; a_child != NULL; a_child = a_child->next) { + if(crm_str_eq((const char *)a_child->name, XML_CIB_TAG_NODE, TRUE)) { + child_name = crm_element_value(a_child, XML_ATTR_UNAME); + if(safe_str_eq(uname, child_name)) { + child_name = ID(a_child); + if(child_name != NULL) { + *uuid = crm_strdup(child_name); + rc = cib_ok; } - ); - free_xml(fragment); - return rc; + break; + } + } + } + free_xml(fragment); + return rc; } enum cib_errors query_node_uname(cib_t *the_cib, const char *uuid, char **uname) { - enum cib_errors rc = cib_ok; - xmlNode *xml_obj = NULL; - xmlNode *fragment = NULL; - const char *child_name = NULL; + enum cib_errors rc = cib_ok; + xmlNode *a_child = NULL; + xmlNode *xml_obj = NULL; + xmlNode *fragment = NULL; + const char *child_name = NULL; - CRM_ASSERT(uname != NULL); - CRM_ASSERT(uuid != NULL); + CRM_ASSERT(uname != NULL); + CRM_ASSERT(uuid != NULL); - rc = the_cib->cmds->query(the_cib, XML_CIB_TAG_NODES, &fragment, - cib_sync_call|cib_scope_local); - if(rc != cib_ok) { - return rc; - } + rc = the_cib->cmds->query(the_cib, XML_CIB_TAG_NODES, &fragment, + cib_sync_call|cib_scope_local); + if(rc != cib_ok) { + return rc; + } - xml_obj = fragment; - CRM_CHECK(safe_str_eq(crm_element_name(xml_obj), XML_CIB_TAG_NODES), - return cib_output_data); - CRM_ASSERT(xml_obj != NULL); - crm_log_xml_debug_2(xml_obj, "Result section"); + xml_obj = fragment; + CRM_CHECK(safe_str_eq(crm_element_name(xml_obj), XML_CIB_TAG_NODES), + return cib_output_data); + CRM_ASSERT(xml_obj != NULL); + crm_log_xml_debug_2(xml_obj, "Result section"); - rc = cib_NOTEXISTS; - *uname = NULL; + rc = cib_NOTEXISTS; + *uname = NULL; - xml_child_iter_filter( - xml_obj, a_child, XML_CIB_TAG_NODE, - child_name = ID(a_child); - - if(safe_str_eq(uuid, child_name)) { - child_name = crm_element_value(a_child, XML_ATTR_UNAME); - if(child_name != NULL) { - *uname = crm_strdup(child_name); - rc = cib_ok; - } - break; + for(a_child = xml_obj; a_child != NULL; a_child = a_child->next) { + if(crm_str_eq((const char *)a_child->name, XML_CIB_TAG_NODE, TRUE)) { + child_name = ID(a_child); + if(safe_str_eq(uuid, child_name)) { + child_name = crm_element_value(a_child, XML_ATTR_UNAME); + if(child_name != NULL) { + *uname = crm_strdup(child_name); + rc = cib_ok; } - ); - free_xml(fragment); - return rc; + break; + } + } + } + + free_xml(fragment); + return rc; } enum cib_errors set_standby(cib_t *the_cib, const char *uuid, const char *scope, const char *standby_value) { - enum cib_errors rc = cib_ok; - int str_length = 3; - char *attr_id = NULL; - char *set_name = NULL; - const char *attr_name = "standby"; - CRM_CHECK(standby_value != NULL, return cib_missing_data); - if(scope == NULL) { - scope = XML_CIB_TAG_NODES; - } + enum cib_errors rc = cib_ok; + int str_length = 3; + char *attr_id = NULL; + char *set_name = NULL; + const char *attr_name = "standby"; + CRM_CHECK(standby_value != NULL, return cib_missing_data); + if(scope == NULL) { + scope = XML_CIB_TAG_NODES; + } - CRM_CHECK(uuid != NULL, return cib_missing_data); - str_length += strlen(attr_name); - str_length += strlen(uuid); + CRM_CHECK(uuid != NULL, return cib_missing_data); + str_length += strlen(attr_name); + str_length += strlen(uuid); - if(safe_str_eq(scope, "reboot") || safe_str_eq(scope, XML_CIB_TAG_STATUS)) { - const char *extra = "transient"; - scope = XML_CIB_TAG_STATUS; + if(safe_str_eq(scope, "reboot") || safe_str_eq(scope, XML_CIB_TAG_STATUS)) { + const char *extra = "transient"; + scope = XML_CIB_TAG_STATUS; - str_length += strlen(extra); - crm_malloc0(attr_id, str_length); - sprintf(attr_id, "%s-%s-%s", extra, attr_name, uuid); + str_length += strlen(extra); + crm_malloc0(attr_id, str_length); + sprintf(attr_id, "%s-%s-%s", extra, attr_name, uuid); - } else { - scope = XML_CIB_TAG_NODES; - crm_malloc0(attr_id, str_length); - sprintf(attr_id, "%s-%s", attr_name, uuid); - } + } else { + scope = XML_CIB_TAG_NODES; + crm_malloc0(attr_id, str_length); + sprintf(attr_id, "%s-%s", attr_name, uuid); + } - rc = update_attr(the_cib, cib_sync_call, scope, uuid, NULL, set_name, - attr_id, attr_name, standby_value, TRUE); + rc = update_attr(the_cib, cib_sync_call, scope, uuid, NULL, set_name, + attr_id, attr_name, standby_value, TRUE); - crm_free(attr_id); - crm_free(set_name); - return rc; + crm_free(attr_id); + crm_free(set_name); + return rc; } diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index a89821e345..35190d980b 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -1,1011 +1,1010 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include enum cib_errors cib_process_query( - const char *op, int options, const char *section, xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - xmlNode *obj_root = NULL; - enum cib_errors result = cib_ok; + xmlNode *obj_root = NULL; + enum cib_errors result = cib_ok; - crm_debug_2("Processing \"%s\" event for section=%s", - op, crm_str(section)); + crm_debug_2("Processing \"%s\" event for section=%s", + op, crm_str(section)); - if(options & cib_xpath) { - return cib_process_xpath(op, options, section, req, input, - existing_cib, result_cib, answer); - } + if(options & cib_xpath) { + return cib_process_xpath(op, options, section, req, input, + existing_cib, result_cib, answer); + } - CRM_CHECK(*answer == NULL, free_xml(*answer)); - *answer = NULL; + CRM_CHECK(*answer == NULL, free_xml(*answer)); + *answer = NULL; - if (safe_str_eq(XML_CIB_TAG_SECTION_ALL, section)) { - section = NULL; - } + if (safe_str_eq(XML_CIB_TAG_SECTION_ALL, section)) { + section = NULL; + } - obj_root = get_object_root(section, existing_cib); + obj_root = get_object_root(section, existing_cib); - if(obj_root == NULL) { - result = cib_NOTEXISTS; + if(obj_root == NULL) { + result = cib_NOTEXISTS; - } else { - *answer = obj_root; - } + } else { + *answer = obj_root; + } - if(result == cib_ok && *answer == NULL) { - crm_err("Error creating query response"); - result = cib_output_data; - } + if(result == cib_ok && *answer == NULL) { + crm_err("Error creating query response"); + result = cib_output_data; + } - return result; + return result; } enum cib_errors cib_process_erase( - const char *op, int options, const char *section, xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - enum cib_errors result = cib_ok; + enum cib_errors result = cib_ok; - crm_debug_2("Processing \"%s\" event", op); - *answer = NULL; - free_xml(*result_cib); - *result_cib = createEmptyCib(); + crm_debug_2("Processing \"%s\" event", op); + *answer = NULL; + free_xml(*result_cib); + *result_cib = createEmptyCib(); - copy_in_properties(*result_cib, existing_cib); - cib_update_counter(*result_cib, XML_ATTR_GENERATION, FALSE); + copy_in_properties(*result_cib, existing_cib); + cib_update_counter(*result_cib, XML_ATTR_GENERATION, FALSE); - return result; + return result; } enum cib_errors cib_process_upgrade( - const char *op, int options, const char *section, xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { int rc = 0; int new_version = 0; int current_version = 0; const char *value = crm_element_value(existing_cib, XML_ATTR_VALIDATION);; *answer = NULL; crm_debug_2("Processing \"%s\" event", op); if(value != NULL) { current_version = get_schema_version(value); } rc = update_validation(result_cib, &new_version, TRUE, TRUE); if(new_version > current_version) { return cib_ok; } return rc; } enum cib_errors cib_process_bump( - const char *op, int options, const char *section, xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - enum cib_errors result = cib_ok; + enum cib_errors result = cib_ok; - crm_debug_2("Processing \"%s\" event for epoch=%s", - op, crm_str(crm_element_value(existing_cib, XML_ATTR_GENERATION))); + crm_debug_2("Processing \"%s\" event for epoch=%s", + op, crm_str(crm_element_value(existing_cib, XML_ATTR_GENERATION))); - *answer = NULL; - cib_update_counter(*result_cib, XML_ATTR_GENERATION, FALSE); + *answer = NULL; + cib_update_counter(*result_cib, XML_ATTR_GENERATION, FALSE); - return result; + return result; } enum cib_errors cib_update_counter(xmlNode *xml_obj, const char *field, gboolean reset) { - char *new_value = NULL; - char *old_value = NULL; - int int_value = -1; + char *new_value = NULL; + char *old_value = NULL; + int int_value = -1; - if(reset == FALSE && crm_element_value(xml_obj, field) != NULL) { - old_value = crm_element_value_copy(xml_obj, field); - } - if(old_value != NULL) { - crm_malloc0(new_value, 128); - int_value = atoi(old_value); - sprintf(new_value, "%d", ++int_value); - } else { - new_value = crm_strdup("1"); - } + if(reset == FALSE && crm_element_value(xml_obj, field) != NULL) { + old_value = crm_element_value_copy(xml_obj, field); + } + if(old_value != NULL) { + crm_malloc0(new_value, 128); + int_value = atoi(old_value); + sprintf(new_value, "%d", ++int_value); + } else { + new_value = crm_strdup("1"); + } - crm_debug_4("%s %d(%s)->%s", - field, int_value, crm_str(old_value), crm_str(new_value)); - crm_xml_add(xml_obj, field, new_value); + crm_debug_4("%s %d(%s)->%s", + field, int_value, crm_str(old_value), crm_str(new_value)); + crm_xml_add(xml_obj, field, new_value); - crm_free(new_value); - crm_free(old_value); + crm_free(new_value); + crm_free(old_value); - return cib_ok; + return cib_ok; } enum cib_errors cib_process_replace( - const char *op, int options, const char *section, xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - const char *tag = NULL; - gboolean verbose = FALSE; - enum cib_errors result = cib_ok; + const char *tag = NULL; + gboolean verbose = FALSE; + enum cib_errors result = cib_ok; - crm_debug_2("Processing \"%s\" event for section=%s", - op, crm_str(section)); + crm_debug_2("Processing \"%s\" event for section=%s", + op, crm_str(section)); - if(options & cib_xpath) { - return cib_process_xpath(op, options, section, req, input, - existing_cib, result_cib, answer); - } + if(options & cib_xpath) { + return cib_process_xpath(op, options, section, req, input, + existing_cib, result_cib, answer); + } - *answer = NULL; + *answer = NULL; - if (input == NULL) { - return cib_NOOBJECT; - } + if (input == NULL) { + return cib_NOOBJECT; + } - tag = crm_element_name(input); + tag = crm_element_name(input); - if (options & cib_verbose) { - verbose = TRUE; - } - if(safe_str_eq(XML_CIB_TAG_SECTION_ALL, section)) { - section = NULL; + if (options & cib_verbose) { + verbose = TRUE; + } + if(safe_str_eq(XML_CIB_TAG_SECTION_ALL, section)) { + section = NULL; - } else if(safe_str_eq(tag, section)) { - section = NULL; - } + } else if(safe_str_eq(tag, section)) { + section = NULL; + } - if(safe_str_eq(tag, XML_TAG_CIB)) { - int updates = 0; - int epoch = 0; - int admin_epoch = 0; + if(safe_str_eq(tag, XML_TAG_CIB)) { + int updates = 0; + int epoch = 0; + int admin_epoch = 0; - int replace_updates = 0; - int replace_epoch = 0; - int replace_admin_epoch = 0; - const char *reason = NULL; + int replace_updates = 0; + int replace_epoch = 0; + int replace_admin_epoch = 0; + const char *reason = NULL; - cib_version_details( - existing_cib, &admin_epoch, &epoch, &updates); - cib_version_details(input, &replace_admin_epoch, - &replace_epoch, &replace_updates); + cib_version_details( + existing_cib, &admin_epoch, &epoch, &updates); + cib_version_details(input, &replace_admin_epoch, + &replace_epoch, &replace_updates); - if(replace_admin_epoch < admin_epoch) { - reason = XML_ATTR_GENERATION_ADMIN; + if(replace_admin_epoch < admin_epoch) { + reason = XML_ATTR_GENERATION_ADMIN; - } else if(replace_admin_epoch > admin_epoch) { - /* no more checks */ + } else if(replace_admin_epoch > admin_epoch) { + /* no more checks */ - } else if(replace_epoch < epoch) { - reason = XML_ATTR_GENERATION; + } else if(replace_epoch < epoch) { + reason = XML_ATTR_GENERATION; - } else if(replace_epoch > epoch) { - /* no more checks */ + } else if(replace_epoch > epoch) { + /* no more checks */ - } else if(replace_updates < updates) { - reason = XML_ATTR_NUMUPDATES; - } + } else if(replace_updates < updates) { + reason = XML_ATTR_NUMUPDATES; + } - if(reason != NULL) { - crm_warn("Replacement %d.%d.%d not applied to %d.%d.%d:" - " current %s is greater than the replacement", - replace_admin_epoch, replace_epoch, - replace_updates, admin_epoch, epoch, updates, - reason); - result = cib_old_data; - } + if(reason != NULL) { + crm_warn("Replacement %d.%d.%d not applied to %d.%d.%d:" + " current %s is greater than the replacement", + replace_admin_epoch, replace_epoch, + replace_updates, admin_epoch, epoch, updates, + reason); + result = cib_old_data; + } - free_xml(*result_cib); - *result_cib = copy_xml(input); + free_xml(*result_cib); + *result_cib = copy_xml(input); - } else { - xmlNode *obj_root = NULL; - gboolean ok = TRUE; - obj_root = get_object_root(section, *result_cib); - ok = replace_xml_child(NULL, obj_root, input, FALSE); - if(ok == FALSE) { - crm_debug_2("No matching object to replace"); - result = cib_NOTEXISTS; - } + } else { + xmlNode *obj_root = NULL; + gboolean ok = TRUE; + obj_root = get_object_root(section, *result_cib); + ok = replace_xml_child(NULL, obj_root, input, FALSE); + if(ok == FALSE) { + crm_debug_2("No matching object to replace"); + result = cib_NOTEXISTS; } + } - return result; + return result; } enum cib_errors cib_process_delete( - const char *op, int options, const char *section, xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - xmlNode *obj_root = NULL; - crm_debug_2("Processing \"%s\" event", op); + xmlNode *obj_root = NULL; + crm_debug_2("Processing \"%s\" event", op); - if(options & cib_xpath) { - return cib_process_xpath(op, options, section, req, input, - existing_cib, result_cib, answer); - } + if(options & cib_xpath) { + return cib_process_xpath(op, options, section, req, input, + existing_cib, result_cib, answer); + } - if(input == NULL) { - crm_err("Cannot perform modification with no data"); - return cib_NOOBJECT; - } + if(input == NULL) { + crm_err("Cannot perform modification with no data"); + return cib_NOOBJECT; + } - obj_root = get_object_root(section, *result_cib); + obj_root = get_object_root(section, *result_cib); - crm_validate_data(input); - crm_validate_data(*result_cib); + crm_validate_data(input); + crm_validate_data(*result_cib); - if(replace_xml_child(NULL, obj_root, input, TRUE) == FALSE) { - crm_debug_2("No matching object to delete"); - } + if(replace_xml_child(NULL, obj_root, input, TRUE) == FALSE) { + crm_debug_2("No matching object to delete"); + } - return cib_ok; + return cib_ok; } enum cib_errors cib_process_modify( - const char *op, int options, const char *section, xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - xmlNode *obj_root = NULL; - crm_debug_2("Processing \"%s\" event", op); + xmlNode *obj_root = NULL; + crm_debug_2("Processing \"%s\" event", op); - if(options & cib_xpath) { - return cib_process_xpath(op, options, section, req, input, - existing_cib, result_cib, answer); - } + if(options & cib_xpath) { + return cib_process_xpath(op, options, section, req, input, + existing_cib, result_cib, answer); + } - if(input == NULL) { - crm_err("Cannot perform modification with no data"); - return cib_NOOBJECT; - } + if(input == NULL) { + crm_err("Cannot perform modification with no data"); + return cib_NOOBJECT; + } - obj_root = get_object_root(section, *result_cib); + obj_root = get_object_root(section, *result_cib); - crm_validate_data(input); - crm_validate_data(*result_cib); + crm_validate_data(input); + crm_validate_data(*result_cib); - if(obj_root == NULL) { - xmlNode *tmp_section = NULL; - const char *path = get_object_parent(section); - if(path == NULL) { - return cib_bad_section; - } + if(obj_root == NULL) { + xmlNode *tmp_section = NULL; + const char *path = get_object_parent(section); + if(path == NULL) { + return cib_bad_section; + } - tmp_section = create_xml_node(NULL, section); - cib_process_xpath( - CIB_OP_CREATE, 0, path, NULL, tmp_section, NULL, result_cib, answer); - free_xml(tmp_section); + tmp_section = create_xml_node(NULL, section); + cib_process_xpath( + CIB_OP_CREATE, 0, path, NULL, tmp_section, NULL, result_cib, answer); + free_xml(tmp_section); - obj_root = get_object_root(section, *result_cib); - } + obj_root = get_object_root(section, *result_cib); + } - CRM_CHECK(obj_root != NULL, return cib_unknown); + CRM_CHECK(obj_root != NULL, return cib_unknown); - if(update_xml_child(obj_root, input) == FALSE) { - if(options & cib_can_create) { - add_node_copy(obj_root, input); - } else { - return cib_NOTEXISTS; - } + if(update_xml_child(obj_root, input) == FALSE) { + if(options & cib_can_create) { + add_node_copy(obj_root, input); + } else { + return cib_NOTEXISTS; } + } - return cib_ok; + return cib_ok; } static int update_cib_object(xmlNode *parent, xmlNode *update) { - const char *replace = NULL; - const char *object_name = NULL; - const char *object_id = NULL; - xmlNode *target = NULL; - int result = cib_ok; + int result = cib_ok; + xmlNode *target = NULL; + xmlNode *a_child = NULL; + const char *replace = NULL; + const char *object_id = NULL; + const char *object_name = NULL; - CRM_CHECK(update != NULL, return cib_NOOBJECT); - CRM_CHECK(parent != NULL, return cib_NOPARENT); + CRM_CHECK(update != NULL, return cib_NOOBJECT); + CRM_CHECK(parent != NULL, return cib_NOPARENT); - object_name = crm_element_name(update); - CRM_CHECK(object_name != NULL, return cib_NOOBJECT); + object_name = crm_element_name(update); + CRM_CHECK(object_name != NULL, return cib_NOOBJECT); - object_id = ID(update); - crm_debug_3("Processing: <%s id=%s>", - crm_str(object_name), crm_str(object_id)); + object_id = ID(update); + crm_debug_3("Processing: <%s id=%s>", + crm_str(object_name), crm_str(object_id)); - if(object_id == NULL) { - /* placeholder object */ - target = find_xml_node(parent, object_name, FALSE); + if(object_id == NULL) { + /* placeholder object */ + target = find_xml_node(parent, object_name, FALSE); - } else { - target = find_entity(parent, object_name, object_id); - } + } else { + target = find_entity(parent, object_name, object_id); + } - if(target == NULL) { - target = create_xml_node(parent, object_name); - } - - crm_debug_2("Found node <%s id=%s> to update", - crm_str(object_name), crm_str(object_id)); - - replace = crm_element_value(update, XML_CIB_ATTR_REPLACE); - if(replace != NULL) { - xmlNode *remove = NULL; - int last = 0, lpc = 0, len = 0; - - len = strlen(replace); - while(lpc <= len) { - if(replace[lpc] == ',' || replace[lpc] == 0) { - char *replace_item = NULL; - if ( last == lpc ) { - /* nothing to do */ - last = lpc+1; - goto incr; - } - - crm_malloc0(replace_item, lpc - last + 1); - strncpy(replace_item, replace+last, lpc-last); - - remove = find_xml_node(target, replace_item, FALSE); - if(remove != NULL) { - crm_debug_3("Replacing node <%s> in <%s>", - replace_item, crm_element_name(target)); - zap_xml_from_parent(target, remove); - } - crm_free(replace_item); + if(target == NULL) { + target = create_xml_node(parent, object_name); + } + + crm_debug_2("Found node <%s id=%s> to update", + crm_str(object_name), crm_str(object_id)); + + replace = crm_element_value(update, XML_CIB_ATTR_REPLACE); + if(replace != NULL) { + xmlNode *remove = NULL; + int last = 0, lpc = 0, len = 0; + + len = strlen(replace); + while(lpc <= len) { + if(replace[lpc] == ',' || replace[lpc] == 0) { + char *replace_item = NULL; + if ( last == lpc ) { + /* nothing to do */ last = lpc+1; + goto incr; } - incr: - lpc++; + + crm_malloc0(replace_item, lpc - last + 1); + strncpy(replace_item, replace+last, lpc-last); + + remove = find_xml_node(target, replace_item, FALSE); + if(remove != NULL) { + crm_debug_3("Replacing node <%s> in <%s>", + replace_item, crm_element_name(target)); + zap_xml_from_parent(target, remove); + } + crm_free(replace_item); + last = lpc+1; } - xml_remove_prop(update, XML_CIB_ATTR_REPLACE); - xml_remove_prop(target, XML_CIB_ATTR_REPLACE); + incr: + lpc++; } + xml_remove_prop(update, XML_CIB_ATTR_REPLACE); + xml_remove_prop(target, XML_CIB_ATTR_REPLACE); + } - copy_in_properties(target, update); + copy_in_properties(target, update); - crm_debug_3("Processing children of <%s id=%s>", - crm_str(object_name), crm_str(object_id)); + crm_debug_3("Processing children of <%s id=%s>", + crm_str(object_name), crm_str(object_id)); - xml_child_iter( - update, a_child, - int tmp_result = 0; - crm_debug_3("Updating child <%s id=%s>", - crm_element_name(a_child), ID(a_child)); + for(a_child = update; a_child != NULL; a_child = a_child->next) { + int tmp_result = 0; + crm_debug_3("Updating child <%s id=%s>", + crm_element_name(a_child), ID(a_child)); - tmp_result = update_cib_object(target, a_child); + tmp_result = update_cib_object(target, a_child); - /* only the first error is likely to be interesting */ - if(tmp_result != cib_ok) { - crm_err("Error updating child <%s id=%s>", - crm_element_name(a_child), ID(a_child)); + /* only the first error is likely to be interesting */ + if(tmp_result != cib_ok) { + crm_err("Error updating child <%s id=%s>", + crm_element_name(a_child), ID(a_child)); - if(result == cib_ok) { - result = tmp_result; - } - } - ); + if(result == cib_ok) { + result = tmp_result; + } + } + } - crm_debug_3("Finished with <%s id=%s>", - crm_str(object_name), crm_str(object_id)); + crm_debug_3("Finished with <%s id=%s>", + crm_str(object_name), crm_str(object_id)); - return result; + return result; } static int add_cib_object(xmlNode *parent, xmlNode *new_obj) { - enum cib_errors result = cib_ok; - const char *object_name = NULL; - const char *object_id = NULL; - xmlNode *equiv_node = NULL; + enum cib_errors result = cib_ok; + const char *object_name = NULL; + const char *object_id = NULL; + xmlNode *equiv_node = NULL; - if(new_obj != NULL) { - object_name = crm_element_name(new_obj); - } - object_id = crm_element_value(new_obj, XML_ATTR_ID); + if(new_obj != NULL) { + object_name = crm_element_name(new_obj); + } + object_id = crm_element_value(new_obj, XML_ATTR_ID); - crm_debug_3("Processing: <%s id=%s>", - crm_str(object_name), crm_str(object_id)); + crm_debug_3("Processing: <%s id=%s>", + crm_str(object_name), crm_str(object_id)); - if(new_obj == NULL || object_name == NULL) { - result = cib_NOOBJECT; + if(new_obj == NULL || object_name == NULL) { + result = cib_NOOBJECT; - } else if(parent == NULL) { - result = cib_NOPARENT; + } else if(parent == NULL) { + result = cib_NOPARENT; - } else if(object_id == NULL) { - /* placeholder object */ - equiv_node = find_xml_node(parent, object_name, FALSE); + } else if(object_id == NULL) { + /* placeholder object */ + equiv_node = find_xml_node(parent, object_name, FALSE); - } else { - equiv_node = find_entity(parent, object_name, object_id); - } + } else { + equiv_node = find_entity(parent, object_name, object_id); + } - if(result != cib_ok) { - ; /* do nothing */ + if(result != cib_ok) { + ; /* do nothing */ - } else if(equiv_node != NULL) { - result = cib_EXISTS; + } else if(equiv_node != NULL) { + result = cib_EXISTS; - } else { - result = update_cib_object(parent, new_obj); - } + } else { + result = update_cib_object(parent, new_obj); + } - return result; + return result; } enum cib_errors cib_process_create( const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { xmlNode *failed = NULL; enum cib_errors result = cib_ok; xmlNode *update_section = NULL; crm_debug_2("Processing \"%s\" event for section=%s", op, crm_str(section)); if(safe_str_eq(XML_CIB_TAG_SECTION_ALL, section)) { section = NULL; } else if(safe_str_eq(XML_TAG_CIB, section)) { section = NULL; } else if(safe_str_eq(crm_element_name(input), XML_TAG_CIB)) { section = NULL; } CRM_CHECK(strcasecmp(CIB_OP_CREATE, op) == 0, return cib_operation); if(input == NULL) { crm_err("Cannot perform modification with no data"); return cib_NOOBJECT; } if(section == NULL) { return cib_process_modify(op, options, section, req, input, existing_cib, result_cib, answer); } failed = create_xml_node(NULL, XML_TAG_FAILED); update_section = get_object_root(section, *result_cib); if(safe_str_eq(crm_element_name(input), section)) { - xml_child_iter(input, a_child, - result = add_cib_object(update_section, a_child); - if(update_results(failed, a_child, op, result)) { - break; - } - ); + xmlNode *a_child = NULL; + for(a_child = input; a_child != NULL; a_child = a_child->next) { + result = add_cib_object(update_section, a_child); + if(update_results(failed, a_child, op, result)) { + break; + } + } } else { result = add_cib_object(update_section, input); update_results(failed, input, op, result); } if(xml_has_children(failed)) { CRM_CHECK(result != cib_ok, result = cib_unknown); } if (result != cib_ok) { crm_log_xml_err(failed, "CIB Update failures"); *answer = failed; } else { free_xml(failed); } return result; } enum cib_errors cib_process_diff( - const char *op, int options, const char *section, xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { - unsigned int log_level = LOG_DEBUG; - const char *reason = NULL; - gboolean apply_diff = TRUE; - enum cib_errors result = cib_ok; - - int this_updates = 0; - int this_epoch = 0; - int this_admin_epoch = 0; - - int diff_add_updates = 0; - int diff_add_epoch = 0; - int diff_add_admin_epoch = 0; - - int diff_del_updates = 0; - int diff_del_epoch = 0; - int diff_del_admin_epoch = 0; - - crm_debug_2("Processing \"%s\" event", op); - - cib_diff_version_details( - input, - &diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates, - &diff_del_admin_epoch, &diff_del_epoch, &diff_del_updates); - - crm_element_value_int(existing_cib, XML_ATTR_GENERATION, &this_epoch); - crm_element_value_int(existing_cib, XML_ATTR_NUMUPDATES, &this_updates); - crm_element_value_int(existing_cib, XML_ATTR_GENERATION_ADMIN, &this_admin_epoch); - - if(this_epoch < 0) { this_epoch = 0; } - if(this_updates < 0) { this_updates = 0; } - if(this_admin_epoch < 0) { this_admin_epoch = 0; } - - if(diff_del_admin_epoch == diff_add_admin_epoch - && diff_del_epoch == diff_add_epoch - && diff_del_updates == diff_add_updates) { - if(options & cib_force_diff) { - apply_diff = FALSE; - log_level = LOG_ERR; - reason = "+ and - versions in the diff did not change in global update"; - crm_log_xml_warn(input, "Bad global update"); + unsigned int log_level = LOG_DEBUG; + const char *reason = NULL; + gboolean apply_diff = TRUE; + enum cib_errors result = cib_ok; + + int this_updates = 0; + int this_epoch = 0; + int this_admin_epoch = 0; + + int diff_add_updates = 0; + int diff_add_epoch = 0; + int diff_add_admin_epoch = 0; + + int diff_del_updates = 0; + int diff_del_epoch = 0; + int diff_del_admin_epoch = 0; + + crm_debug_2("Processing \"%s\" event", op); + + cib_diff_version_details( + input, + &diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates, + &diff_del_admin_epoch, &diff_del_epoch, &diff_del_updates); + + crm_element_value_int(existing_cib, XML_ATTR_GENERATION, &this_epoch); + crm_element_value_int(existing_cib, XML_ATTR_NUMUPDATES, &this_updates); + crm_element_value_int(existing_cib, XML_ATTR_GENERATION_ADMIN, &this_admin_epoch); + + if(this_epoch < 0) { this_epoch = 0; } + if(this_updates < 0) { this_updates = 0; } + if(this_admin_epoch < 0) { this_admin_epoch = 0; } + + if(diff_del_admin_epoch == diff_add_admin_epoch + && diff_del_epoch == diff_add_epoch + && diff_del_updates == diff_add_updates) { + if(options & cib_force_diff) { + apply_diff = FALSE; + log_level = LOG_ERR; + reason = "+ and - versions in the diff did not change in global update"; + crm_log_xml_warn(input, "Bad global update"); - } else if(diff_add_admin_epoch == -1 && diff_add_epoch == -1 && diff_add_updates == -1) { - diff_add_epoch = this_epoch; - diff_add_updates = this_updates + 1; - diff_add_admin_epoch = this_admin_epoch; - diff_del_epoch = this_epoch; - diff_del_updates = this_updates; - diff_del_admin_epoch = this_admin_epoch; - - } else { - apply_diff = FALSE; - log_level = LOG_ERR; - reason = "+ and - versions in the diff did not change"; - log_cib_diff(LOG_ERR, input, __FUNCTION__); - } + } else if(diff_add_admin_epoch == -1 && diff_add_epoch == -1 && diff_add_updates == -1) { + diff_add_epoch = this_epoch; + diff_add_updates = this_updates + 1; + diff_add_admin_epoch = this_admin_epoch; + diff_del_epoch = this_epoch; + diff_del_updates = this_updates; + diff_del_admin_epoch = this_admin_epoch; + + } else { + apply_diff = FALSE; + log_level = LOG_ERR; + reason = "+ and - versions in the diff did not change"; + log_cib_diff(LOG_ERR, input, __FUNCTION__); } + } - if(apply_diff && diff_del_admin_epoch > this_admin_epoch) { - result = cib_diff_resync; - apply_diff = FALSE; - log_level = LOG_INFO; - reason = "current \""XML_ATTR_GENERATION_ADMIN"\" is less than required"; + if(apply_diff && diff_del_admin_epoch > this_admin_epoch) { + result = cib_diff_resync; + apply_diff = FALSE; + log_level = LOG_INFO; + reason = "current \""XML_ATTR_GENERATION_ADMIN"\" is less than required"; - } else if(apply_diff && diff_del_admin_epoch < this_admin_epoch) { - apply_diff = FALSE; - log_level = LOG_WARNING; - reason = "current \""XML_ATTR_GENERATION_ADMIN"\" is greater than required"; - - } else if(apply_diff && diff_del_epoch > this_epoch) { - result = cib_diff_resync; - apply_diff = FALSE; - log_level = LOG_INFO; - reason = "current \""XML_ATTR_GENERATION"\" is less than required"; + } else if(apply_diff && diff_del_admin_epoch < this_admin_epoch) { + apply_diff = FALSE; + log_level = LOG_WARNING; + reason = "current \""XML_ATTR_GENERATION_ADMIN"\" is greater than required"; + + } else if(apply_diff && diff_del_epoch > this_epoch) { + result = cib_diff_resync; + apply_diff = FALSE; + log_level = LOG_INFO; + reason = "current \""XML_ATTR_GENERATION"\" is less than required"; - } else if(apply_diff && diff_del_epoch < this_epoch) { - apply_diff = FALSE; - log_level = LOG_WARNING; - reason = "current \""XML_ATTR_GENERATION"\" is greater than required"; - - } else if(apply_diff && diff_del_updates > this_updates) { - result = cib_diff_resync; - apply_diff = FALSE; - log_level = LOG_INFO; - reason = "current \""XML_ATTR_NUMUPDATES"\" is less than required"; + } else if(apply_diff && diff_del_epoch < this_epoch) { + apply_diff = FALSE; + log_level = LOG_WARNING; + reason = "current \""XML_ATTR_GENERATION"\" is greater than required"; + + } else if(apply_diff && diff_del_updates > this_updates) { + result = cib_diff_resync; + apply_diff = FALSE; + log_level = LOG_INFO; + reason = "current \""XML_ATTR_NUMUPDATES"\" is less than required"; - } else if(apply_diff && diff_del_updates < this_updates) { - apply_diff = FALSE; - log_level = LOG_WARNING; - reason = "current \""XML_ATTR_NUMUPDATES"\" is greater than required"; - } + } else if(apply_diff && diff_del_updates < this_updates) { + apply_diff = FALSE; + log_level = LOG_WARNING; + reason = "current \""XML_ATTR_NUMUPDATES"\" is greater than required"; + } - if(apply_diff) { - free_xml(*result_cib); - *result_cib = NULL; - if(apply_xml_diff(existing_cib, input, result_cib) == FALSE) { - log_level = LOG_NOTICE; - reason = "Failed application of an update diff"; + if(apply_diff) { + free_xml(*result_cib); + *result_cib = NULL; + if(apply_xml_diff(existing_cib, input, result_cib) == FALSE) { + log_level = LOG_NOTICE; + reason = "Failed application of an update diff"; - if(options & cib_force_diff) { - result = cib_diff_resync; - } - } + if(options & cib_force_diff) { + result = cib_diff_resync; + } } + } - if(reason != NULL) { - do_crm_log( - log_level, - "Diff %d.%d.%d -> %d.%d.%d not applied to %d.%d.%d: %s", - diff_del_admin_epoch,diff_del_epoch,diff_del_updates, - diff_add_admin_epoch,diff_add_epoch,diff_add_updates, - this_admin_epoch,this_epoch,this_updates, reason); - - crm_log_xml_trace(input, "Discarded diff"); - if(result == cib_ok) { - result = cib_diff_failed; - } + if(reason != NULL) { + do_crm_log( + log_level, + "Diff %d.%d.%d -> %d.%d.%d not applied to %d.%d.%d: %s", + diff_del_admin_epoch,diff_del_epoch,diff_del_updates, + diff_add_admin_epoch,diff_add_epoch,diff_add_updates, + this_admin_epoch,this_epoch,this_updates, reason); + + crm_log_xml_trace(input, "Discarded diff"); + if(result == cib_ok) { + result = cib_diff_failed; + } - } else if(apply_diff) { - crm_debug_2("Diff %d.%d.%d -> %d.%d.%d was applied to %d.%d.%d", - diff_del_admin_epoch,diff_del_epoch,diff_del_updates, - diff_add_admin_epoch,diff_add_epoch,diff_add_updates, - this_admin_epoch,this_epoch,this_updates); + } else if(apply_diff) { + crm_debug_2("Diff %d.%d.%d -> %d.%d.%d was applied to %d.%d.%d", + diff_del_admin_epoch,diff_del_epoch,diff_del_updates, + diff_add_admin_epoch,diff_add_epoch,diff_add_updates, + this_admin_epoch,this_epoch,this_updates); - } - return result; + } + return result; } gboolean apply_cib_diff(xmlNode *old, xmlNode *diff, xmlNode **new) { - gboolean result = TRUE; - const char *value = NULL; - - int this_updates = 0; - int this_epoch = 0; - int this_admin_epoch = 0; - - int diff_add_updates = 0; - int diff_add_epoch = 0; - int diff_add_admin_epoch = 0; - - int diff_del_updates = 0; - int diff_del_epoch = 0; - int diff_del_admin_epoch = 0; - - CRM_CHECK(diff != NULL, return FALSE); - CRM_CHECK(old != NULL, return FALSE); - - value = crm_element_value(old, XML_ATTR_GENERATION_ADMIN); - this_admin_epoch = crm_parse_int(value, "0"); - crm_debug_3("%s=%d (%s)", XML_ATTR_GENERATION_ADMIN, - this_admin_epoch, value); - - value = crm_element_value(old, XML_ATTR_GENERATION); - this_epoch = crm_parse_int(value, "0"); - crm_debug_3("%s=%d (%s)", XML_ATTR_GENERATION, this_epoch, value); - - value = crm_element_value(old, XML_ATTR_NUMUPDATES); - this_updates = crm_parse_int(value, "0"); - crm_debug_3("%s=%d (%s)", XML_ATTR_NUMUPDATES, this_updates, value); - - cib_diff_version_details( - diff, - &diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates, - &diff_del_admin_epoch, &diff_del_epoch, &diff_del_updates); - - value = NULL; - if(result && diff_del_admin_epoch != this_admin_epoch) { - value = XML_ATTR_GENERATION_ADMIN; - result = FALSE; - crm_debug_3("%s=%d", value, diff_del_admin_epoch); - - } else if(result && diff_del_epoch != this_epoch) { - value = XML_ATTR_GENERATION; - result = FALSE; - crm_debug_3("%s=%d", value, diff_del_epoch); - - } else if(result && diff_del_updates != this_updates) { - value = XML_ATTR_NUMUPDATES; - result = FALSE; - crm_debug_3("%s=%d", value, diff_del_updates); - } + gboolean result = TRUE; + const char *value = NULL; + + int this_updates = 0; + int this_epoch = 0; + int this_admin_epoch = 0; + + int diff_add_updates = 0; + int diff_add_epoch = 0; + int diff_add_admin_epoch = 0; + + int diff_del_updates = 0; + int diff_del_epoch = 0; + int diff_del_admin_epoch = 0; + + CRM_CHECK(diff != NULL, return FALSE); + CRM_CHECK(old != NULL, return FALSE); + + value = crm_element_value(old, XML_ATTR_GENERATION_ADMIN); + this_admin_epoch = crm_parse_int(value, "0"); + crm_debug_3("%s=%d (%s)", XML_ATTR_GENERATION_ADMIN, + this_admin_epoch, value); + + value = crm_element_value(old, XML_ATTR_GENERATION); + this_epoch = crm_parse_int(value, "0"); + crm_debug_3("%s=%d (%s)", XML_ATTR_GENERATION, this_epoch, value); + + value = crm_element_value(old, XML_ATTR_NUMUPDATES); + this_updates = crm_parse_int(value, "0"); + crm_debug_3("%s=%d (%s)", XML_ATTR_NUMUPDATES, this_updates, value); + + cib_diff_version_details( + diff, + &diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates, + &diff_del_admin_epoch, &diff_del_epoch, &diff_del_updates); + + value = NULL; + if(result && diff_del_admin_epoch != this_admin_epoch) { + value = XML_ATTR_GENERATION_ADMIN; + result = FALSE; + crm_debug_3("%s=%d", value, diff_del_admin_epoch); + + } else if(result && diff_del_epoch != this_epoch) { + value = XML_ATTR_GENERATION; + result = FALSE; + crm_debug_3("%s=%d", value, diff_del_epoch); + + } else if(result && diff_del_updates != this_updates) { + value = XML_ATTR_NUMUPDATES; + result = FALSE; + crm_debug_3("%s=%d", value, diff_del_updates); + } - if(result) { - xmlNode *tmp = NULL; - xmlNode *diff_copy = copy_xml(diff); + if(result) { + xmlNode *tmp = NULL; + xmlNode *diff_copy = copy_xml(diff); - tmp = find_xml_node(diff_copy, "diff-removed", TRUE); - if(tmp != NULL) { - xml_remove_prop(tmp, XML_ATTR_GENERATION_ADMIN); - xml_remove_prop(tmp, XML_ATTR_GENERATION); - xml_remove_prop(tmp, XML_ATTR_NUMUPDATES); - } + tmp = find_xml_node(diff_copy, "diff-removed", TRUE); + if(tmp != NULL) { + xml_remove_prop(tmp, XML_ATTR_GENERATION_ADMIN); + xml_remove_prop(tmp, XML_ATTR_GENERATION); + xml_remove_prop(tmp, XML_ATTR_NUMUPDATES); + } - tmp = find_xml_node(diff_copy, "diff-added", TRUE); - if(tmp != NULL) { - xml_remove_prop(tmp, XML_ATTR_GENERATION_ADMIN); - xml_remove_prop(tmp, XML_ATTR_GENERATION); - xml_remove_prop(tmp, XML_ATTR_NUMUPDATES); - } + tmp = find_xml_node(diff_copy, "diff-added", TRUE); + if(tmp != NULL) { + xml_remove_prop(tmp, XML_ATTR_GENERATION_ADMIN); + xml_remove_prop(tmp, XML_ATTR_GENERATION); + xml_remove_prop(tmp, XML_ATTR_NUMUPDATES); + } - result = apply_xml_diff(old, diff_copy, new); - free_xml(diff_copy); + result = apply_xml_diff(old, diff_copy, new); + free_xml(diff_copy); - } else { - crm_err("target and diff %s values didnt match", value); - } + } else { + crm_err("target and diff %s values didnt match", value); + } - return result; + return result; } gboolean cib_config_changed(xmlNode *last, xmlNode *next, xmlNode **diff) { gboolean config_changes = FALSE; xmlXPathObject *xpathObj = NULL; CRM_ASSERT(diff != NULL); if(last != NULL && next != NULL) { *diff = diff_xml_object(last, next, FALSE); } if(*diff == NULL) { goto done; } xpathObj = xpath_search(*diff, "//"XML_CIB_TAG_CONFIGURATION); if(xpathObj && xpathObj->nodesetval->nodeNr > 0) { config_changes = TRUE; goto done; } else if(xpathObj) { xmlXPathFreeObject(xpathObj); } xpathObj = xpath_search(*diff, "//"XML_TAG_DIFF_REMOVED"//"XML_TAG_CIB); if(xpathObj) { int lpc = 0, max = xpathObj->nodesetval->nodeNr; for(lpc = 0; lpc < max; lpc++) { xmlNode *top = getXpathResult(xpathObj, lpc); if(crm_element_value(top, XML_ATTR_GENERATION) != NULL) { config_changes = TRUE; goto done; } if(crm_element_value(top, XML_ATTR_GENERATION_ADMIN) != NULL) { config_changes = TRUE; goto done; } if(crm_element_value(top, XML_ATTR_VALIDATION) != NULL) { config_changes = TRUE; goto done; } if(crm_element_value(top, XML_ATTR_CRM_VERSION) != NULL) { config_changes = TRUE; goto done; } } } done: if(xpathObj) { xmlXPathFreeObject(xpathObj); } return config_changes; } xmlNode * diff_cib_object(xmlNode *old_cib, xmlNode *new_cib, gboolean suppress) { - xmlNode *dest = NULL; - xmlNode *src = NULL; - const char *name = NULL; - const char *value = NULL; - - xmlNode *diff = diff_xml_object(old_cib, new_cib, suppress); - - /* add complete version information */ - src = old_cib; - dest = find_xml_node(diff, "diff-removed", FALSE); - if(src != NULL && dest != NULL) { - name = XML_ATTR_GENERATION_ADMIN; - value = crm_element_value(src, name); - if(value == NULL) { - value = "0"; - } - crm_xml_add(dest, name, value); + xmlNode *dest = NULL; + xmlNode *src = NULL; + const char *name = NULL; + const char *value = NULL; - name = XML_ATTR_GENERATION; - value = crm_element_value(src, name); - if(value == NULL) { - value = "0"; - } - crm_xml_add(dest, name, value); + xmlNode *diff = diff_xml_object(old_cib, new_cib, suppress); + + /* add complete version information */ + src = old_cib; + dest = find_xml_node(diff, "diff-removed", FALSE); + if(src != NULL && dest != NULL) { + name = XML_ATTR_GENERATION_ADMIN; + value = crm_element_value(src, name); + if(value == NULL) { + value = "0"; + } + crm_xml_add(dest, name, value); - name = XML_ATTR_NUMUPDATES; - value = crm_element_value(src, name); - if(value == NULL) { - value = "0"; - } - crm_xml_add(dest, name, value); + name = XML_ATTR_GENERATION; + value = crm_element_value(src, name); + if(value == NULL) { + value = "0"; } + crm_xml_add(dest, name, value); + + name = XML_ATTR_NUMUPDATES; + value = crm_element_value(src, name); + if(value == NULL) { + value = "0"; + } + crm_xml_add(dest, name, value); + } - src = new_cib; - dest = find_xml_node(diff, "diff-added", FALSE); - if(src != NULL && dest != NULL) { - name = XML_ATTR_GENERATION_ADMIN; - value = crm_element_value(src, name); - if(value == NULL) { - value = "0"; - } - crm_xml_add(dest, name, value); + src = new_cib; + dest = find_xml_node(diff, "diff-added", FALSE); + if(src != NULL && dest != NULL) { + name = XML_ATTR_GENERATION_ADMIN; + value = crm_element_value(src, name); + if(value == NULL) { + value = "0"; + } + crm_xml_add(dest, name, value); - name = XML_ATTR_GENERATION; - value = crm_element_value(src, name); - if(value == NULL) { - value = "0"; - } - crm_xml_add(dest, name, value); + name = XML_ATTR_GENERATION; + value = crm_element_value(src, name); + if(value == NULL) { + value = "0"; + } + crm_xml_add(dest, name, value); - name = XML_ATTR_NUMUPDATES; - value = crm_element_value(src, name); - if(value == NULL) { - value = "0"; - } - crm_xml_add(dest, name, value); + name = XML_ATTR_NUMUPDATES; + value = crm_element_value(src, name); + if(value == NULL) { + value = "0"; } - return diff; + crm_xml_add(dest, name, value); + } + return diff; } enum cib_errors cib_process_xpath( - const char *op, int options, const char *section, xmlNode *req, xmlNode *input, - xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) + const char *op, int options, const char *section, xmlNode *req, xmlNode *input, + xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer) { int lpc = 0; int max = 0; int rc = cib_ok; gboolean is_query = safe_str_eq(op, CIB_OP_QUERY); xmlXPathObjectPtr xpathObj = NULL; crm_debug_2("Processing \"%s\" event", op); if(is_query) { xpathObj = xpath_search(existing_cib, section); } else { xpathObj = xpath_search(*result_cib, section); } if(xpathObj != NULL && xpathObj->nodesetval != NULL) { max = xpathObj->nodesetval->nodeNr; } if(max < 1 && safe_str_eq(op, CIB_OP_DELETE)) { crm_debug("%s was already removed", section); } else if(max < 1) { crm_debug("%s: %s does not exist", op, section); rc = cib_NOTEXISTS; } else if(is_query) { if(max > 1) { *answer = create_xml_node(NULL, "xpath-query"); } } for(lpc = 0; lpc < max; lpc++) { xmlChar *path = NULL; xmlNode *match = getXpathResult(xpathObj, lpc); if(match == NULL) { continue; } path = xmlGetNodePath(match); crm_debug("Processing %s op for %s (%s)", op, section, path); free(path); if(safe_str_eq(op, CIB_OP_DELETE)) { free_xml_from_parent(NULL, match); if((options & cib_multiple) == 0) { break; } } else if(safe_str_eq(op, CIB_OP_MODIFY)) { if(update_xml_child(match, input) == FALSE) { rc = cib_NOTEXISTS; } else if((options & cib_multiple) == 0) { break; } } else if(safe_str_eq(op, CIB_OP_CREATE)) { add_node_copy(match, input); break; } else if(safe_str_eq(op, CIB_OP_QUERY)) { if(options & cib_no_children) { const char *tag = TYPE(match); xmlNode *shallow = create_xml_node(*answer, tag); copy_in_properties(shallow, match); if(*answer == NULL) { *answer = shallow; } } else if(*answer) { add_node_copy(*answer, match); } else { *answer = match; } } else if(safe_str_eq(op, CIB_OP_REPLACE)) { xmlNode *parent = match->parent; free_xml_from_parent(NULL, match); if(input != NULL) { add_node_copy(parent, input); } if((options & cib_multiple) == 0) { break; } } } if(xpathObj) { xmlXPathFreeObject(xpathObj); } return rc; } /* remove this function */ gboolean update_results( - xmlNode *failed, xmlNode *target, const char* operation, int return_code) + xmlNode *failed, xmlNode *target, const char* operation, int return_code) { - xmlNode *xml_node = NULL; - gboolean was_error = FALSE; - const char *error_msg = NULL; + xmlNode *xml_node = NULL; + gboolean was_error = FALSE; + const char *error_msg = NULL; - if (return_code != cib_ok) { - error_msg = cib_error2string(return_code); - - xml_node = create_xml_node(failed, XML_FAIL_TAG_CIB); + if (return_code != cib_ok) { + error_msg = cib_error2string(return_code); - was_error = TRUE; - - add_node_copy(xml_node, target); + was_error = TRUE; + xml_node = create_xml_node(failed, XML_FAIL_TAG_CIB); + add_node_copy(xml_node, target); - crm_xml_add(xml_node, XML_FAILCIB_ATTR_ID, ID(target)); - crm_xml_add(xml_node, XML_FAILCIB_ATTR_OBJTYPE, TYPE(target)); - crm_xml_add(xml_node, XML_FAILCIB_ATTR_OP, operation); - crm_xml_add(xml_node, XML_FAILCIB_ATTR_REASON, error_msg); + crm_xml_add(xml_node, XML_FAILCIB_ATTR_ID, ID(target)); + crm_xml_add(xml_node, XML_FAILCIB_ATTR_OBJTYPE, TYPE(target)); + crm_xml_add(xml_node, XML_FAILCIB_ATTR_OP, operation); + crm_xml_add(xml_node, XML_FAILCIB_ATTR_REASON, error_msg); - crm_warn("Action %s failed: %s (cde=%d)", - operation, error_msg, return_code); - } + crm_warn("Action %s failed: %s (cde=%d)", + operation, error_msg, return_code); + } - return was_error; + return was_error; } diff --git a/lib/cib/cib_version.c b/lib/cib/cib_version.c index 866bae1b2f..8d82bd11ac 100644 --- a/lib/cib/cib_version.c +++ b/lib/cib/cib_version.c @@ -1,164 +1,165 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include const char * feature_sets[] = { - "1.1", - "1.2", - "1.3", - "2.0", + "1.1", + "1.2", + "1.3", + "2.0", }; typedef struct tag_set_s { - int length; - const char **tags; + int length; + const char **tags; } tag_set_t; const char *feature_tags_12[] = { "master_slave", }; const char *feature_tags_20[] = { "cluster_property_set", "meta_attributes", "transient_attributes", }; tag_set_t feature_tags[] = { - { 0, NULL }, - { 1, feature_tags_12 }, - { 0, NULL }, - { 3, feature_tags_20 }, + { 0, NULL }, + { 1, feature_tags_12 }, + { 0, NULL }, + { 3, feature_tags_20 }, }; const char *feature_attrs_12[] = { "master_node_max", }; const char *feature_attrs_20[] = { "start_delay", "disabled", "on_fail", "prereq", "collocated", "globally_unique", "from_role", "to_role", "node_attribute", "score_attribute", "hours", "transition_key", "op_digest", "op_restart_digest", "op_force_restart", }; tag_set_t feature_attrs[] = { - { 0, NULL }, - { 1, feature_attrs_12 }, - { 0, NULL }, - { 15, feature_attrs_20 }, + { 0, NULL }, + { 1, feature_attrs_12 }, + { 0, NULL }, + { 15, feature_attrs_20 }, }; static int internal_update_feature_set(xmlNode *xml_obj, int current) { - int lpc = current; - int lpc_nested = 0; - const char *value = NULL; - int num_sets = DIMOF(feature_sets); - - CRM_CHECK(compare_version( - CIB_FEATURE_SET, feature_sets[num_sets-1]) == 0, - return num_sets-1); - - for(;lpc < num_sets; lpc++) { - const char *tag = crm_element_name(xml_obj); - crm_debug_3("Checking set %d with %d tags", lpc, - feature_tags[lpc].length); + int lpc = current; + int lpc_nested = 0; + const char *value = NULL; + xmlNode *xml_child = NULL; + int num_sets = DIMOF(feature_sets); + + CRM_CHECK(compare_version( + CIB_FEATURE_SET, feature_sets[num_sets-1]) == 0, + return num_sets-1); + + for(;lpc < num_sets; lpc++) { + const char *tag = crm_element_name(xml_obj); + crm_debug_3("Checking set %d with %d tags", lpc, + feature_tags[lpc].length); - lpc_nested = 0; - for(; lpc_nested < feature_tags[lpc].length; lpc_nested++) { - const char *name = feature_tags[lpc].tags[lpc_nested]; - crm_debug_4("Checking %s vs. %s", tag, name); - if(safe_str_eq(tag, name)) { - crm_debug_2("Found feature %s from set %s", - tag, feature_sets[lpc]); - current = lpc; - break; - } - } - if(current == lpc) { - continue; - } - - lpc_nested = 0; - for(; lpc_nested < feature_attrs[lpc].length; lpc_nested++) { - const char *name = feature_attrs[lpc].tags[lpc_nested]; - crm_debug_4("Checking for %s", name); - value = crm_element_value(xml_obj, name); - if(value != NULL) { - crm_info("Found feature '%s' from set %s", - name, feature_sets[lpc]); - current = lpc; - break; - } - } + lpc_nested = 0; + for(; lpc_nested < feature_tags[lpc].length; lpc_nested++) { + const char *name = feature_tags[lpc].tags[lpc_nested]; + crm_debug_4("Checking %s vs. %s", tag, name); + if(safe_str_eq(tag, name)) { + crm_debug_2("Found feature %s from set %s", + tag, feature_sets[lpc]); + current = lpc; + break; + } + } + if(current == lpc) { + continue; } - if(current == (num_sets -1)) { - return current; + lpc_nested = 0; + for(; lpc_nested < feature_attrs[lpc].length; lpc_nested++) { + const char *name = feature_attrs[lpc].tags[lpc_nested]; + crm_debug_4("Checking for %s", name); + value = crm_element_value(xml_obj, name); + if(value != NULL) { + crm_info("Found feature '%s' from set %s", + name, feature_sets[lpc]); + current = lpc; + break; + } } + } + + if(current == (num_sets -1)) { + return current; + } - xml_child_iter(xml_obj, xml_child, - current = internal_update_feature_set(xml_child,current); - if(current == (num_sets -1)) { - return current; - } - ); - return current; + for(xml_child = xml_obj; xml_child != NULL; xml_child = xml_child->next) { + current = internal_update_feature_set(xml_child,current); + if(current == (num_sets -1)) { + return current; + } + } + return current; } const char * feature_set(xmlNode *xml_obj) { - int set = internal_update_feature_set(xml_obj, 0); - CRM_ASSERT(set >= 0); - CRM_ASSERT(set < DIMOF(feature_sets)); - return feature_sets[set]; + int set = internal_update_feature_set(xml_obj, 0); + CRM_ASSERT(set >= 0); + CRM_ASSERT(set < DIMOF(feature_sets)); + return feature_sets[set]; } diff --git a/lib/common/ais.c b/lib/common/ais.c index ece955781b..258f8dafef 100644 --- a/lib/common/ais.c +++ b/lib/common/ais.c @@ -1,1219 +1,1225 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include "stack.h" #ifdef SUPPORT_COROSYNC # include #endif #ifdef SUPPORT_CMAN # include cman_handle_t pcmk_cman_handle = NULL; #endif #ifdef SUPPORT_CS_QUORUM # include # include # include # include # include quorum_handle_t pcmk_quorum_handle = 0; cpg_handle_t pcmk_cpg_handle = 0; struct cpg_name pcmk_cpg_group = { .length = 0, .value[0] = 0, }; #endif static char *pcmk_uname = NULL; static int pcmk_uname_len = 0; static uint32_t pcmk_nodeid = 0; #define cs_repeat(counter, max, code) do { \ code; \ if(rc == CS_ERR_TRY_AGAIN) { \ counter++; \ crm_debug("Retrying operation after %ds", counter); \ sleep(counter); \ } \ } while(rc == CS_ERR_TRY_AGAIN && counter < max) enum crm_ais_msg_types text2msg_type(const char *text) { int type = crm_msg_none; CRM_CHECK(text != NULL, return type); if(safe_str_eq(text, "ais")) { type = crm_msg_ais; } else if(safe_str_eq(text, "crm_plugin")) { type = crm_msg_ais; } else if(safe_str_eq(text, CRM_SYSTEM_CIB)) { type = crm_msg_cib; } else if(safe_str_eq(text, CRM_SYSTEM_CRMD)) { type = crm_msg_crmd; } else if(safe_str_eq(text, CRM_SYSTEM_DC)) { type = crm_msg_crmd; } else if(safe_str_eq(text, CRM_SYSTEM_TENGINE)) { type = crm_msg_te; } else if(safe_str_eq(text, CRM_SYSTEM_PENGINE)) { type = crm_msg_pe; } else if(safe_str_eq(text, CRM_SYSTEM_LRMD)) { type = crm_msg_lrmd; } else if(safe_str_eq(text, CRM_SYSTEM_STONITHD)) { type = crm_msg_stonithd; } else if(safe_str_eq(text, "stonith-ng")) { type = crm_msg_stonith_ng; } else if(safe_str_eq(text, "attrd")) { type = crm_msg_attrd; } else { /* This will normally be a transient client rather than * a cluster daemon. Set the type to the pid of the client */ int scan_rc = sscanf(text, "%d", &type); if(scan_rc != 1) { /* Ensure its sane */ type = crm_msg_none; } } return type; } char *get_ais_data(const AIS_Message *msg) { int rc = BZ_OK; char *uncompressed = NULL; unsigned int new_size = msg->size + 1; if(msg->is_compressed == FALSE) { crm_debug_2("Returning uncompressed message data"); uncompressed = strdup(msg->data); } else { crm_debug_2("Decompressing message data"); crm_malloc0(uncompressed, new_size); rc = BZ2_bzBuffToBuffDecompress( uncompressed, &new_size, (char*)msg->data, msg->compressed_size, 1, 0); CRM_ASSERT(rc == BZ_OK); CRM_ASSERT(new_size == msg->size); } return uncompressed; } #if SUPPORT_COROSYNC int ais_fd_sync = -1; int ais_fd_async = -1; /* never send messages via this channel */ void *ais_ipc_ctx = NULL; hdb_handle_t ais_ipc_handle = 0; GFDSource *ais_source = NULL; GFDSource *ais_source_sync = NULL; GFDSource *cman_source = NULL; GFDSource *cpg_source = NULL; GFDSource *quorumd_source = NULL; static char *ais_cluster_name = NULL; gboolean get_ais_nodeid(uint32_t *id, char **uname) { struct iovec iov; int retries = 0; int rc = CS_OK; coroipc_response_header_t header; struct crm_ais_nodeid_resp_s answer; header.error = CS_OK; header.id = crm_class_nodeid; header.size = sizeof(coroipc_response_header_t); CRM_CHECK(id != NULL, return FALSE); CRM_CHECK(uname != NULL, return FALSE); iov.iov_base = &header; iov.iov_len = header.size; retry: errno = 0; rc = coroipcc_msg_send_reply_receive( ais_ipc_handle, &iov, 1, &answer, sizeof (answer)); if(rc == CS_OK) { CRM_CHECK(answer.header.size == sizeof (struct crm_ais_nodeid_resp_s), crm_err("Odd message: id=%d, size=%d, error=%d", answer.header.id, answer.header.size, answer.header.error)); CRM_CHECK(answer.header.id == crm_class_nodeid, crm_err("Bad response id: %d", answer.header.id)); } if(rc == CS_ERR_TRY_AGAIN && retries < 20) { retries++; crm_info("Peer overloaded: Re-sending message (Attempt %d of 20)", retries); sleep(retries); /* Proportional back off */ goto retry; } if(rc != CS_OK) { crm_err("Sending nodeid request: FAILED (rc=%d): %s", rc, ais_error2text(rc)); return FALSE; } else if(answer.header.error != CS_OK) { crm_err("Bad response from peer: (rc=%d): %s", rc, ais_error2text(rc)); return FALSE; } crm_info("Server details: id=%u uname=%s cname=%s", answer.id, answer.uname, answer.cname); *id = answer.id; *uname = crm_strdup(answer.uname); ais_cluster_name = crm_strdup(answer.cname); return TRUE; } gboolean crm_get_cluster_name(char **cname) { CRM_CHECK(cname != NULL, return FALSE); if(ais_cluster_name) { *cname = crm_strdup(ais_cluster_name); return TRUE; } return FALSE; } gboolean send_ais_text(int class, const char *data, gboolean local, const char *node, enum crm_ais_msg_types dest) { static int msg_id = 0; static int local_pid = 0; enum cluster_type_e cluster_type = get_cluster_type(); int retries = 0; int rc = CS_OK; int buf_len = sizeof(coroipc_response_header_t); char *buf = NULL; struct iovec iov; const char *transport = "pcmk"; coroipc_response_header_t *header = NULL; AIS_Message *ais_msg = NULL; enum crm_ais_msg_types sender = text2msg_type(crm_system_name); /* There are only 6 handlers registered to crm_lib_service in plugin.c */ CRM_CHECK(class < 6, crm_err("Invalid message class: %d", class); return FALSE); if(data == NULL) { data = ""; } if(local_pid == 0) { local_pid = getpid(); } if(sender == crm_msg_none) { sender = local_pid; } crm_malloc0(ais_msg, sizeof(AIS_Message)); ais_msg->id = msg_id++; ais_msg->header.id = class; ais_msg->header.error = CS_OK; ais_msg->host.type = dest; ais_msg->host.local = local; if(node) { ais_msg->host.size = strlen(node); memset(ais_msg->host.uname, 0, MAX_NAME); memcpy(ais_msg->host.uname, node, ais_msg->host.size); ais_msg->host.id = 0; } else { ais_msg->host.size = 0; memset(ais_msg->host.uname, 0, MAX_NAME); ais_msg->host.id = 0; } ais_msg->sender.id = 0; ais_msg->sender.type = sender; ais_msg->sender.pid = local_pid; ais_msg->sender.size = pcmk_uname_len; memset(ais_msg->sender.uname, 0, MAX_NAME); memcpy(ais_msg->sender.uname, pcmk_uname, ais_msg->sender.size); ais_msg->size = 1 + strlen(data); if(ais_msg->size < CRM_BZ2_THRESHOLD) { failback: crm_realloc(ais_msg, sizeof(AIS_Message) + ais_msg->size); memcpy(ais_msg->data, data, ais_msg->size); } else { char *compressed = NULL; char *uncompressed = crm_strdup(data); unsigned int len = (ais_msg->size * 1.1) + 600; /* recomended size */ crm_debug_5("Compressing message payload"); crm_malloc(compressed, len); rc = BZ2_bzBuffToBuffCompress( compressed, &len, uncompressed, ais_msg->size, CRM_BZ2_BLOCKS, 0, CRM_BZ2_WORK); crm_free(uncompressed); if(rc != BZ_OK) { crm_err("Compression failed: %d", rc); crm_free(compressed); goto failback; } crm_realloc(ais_msg, sizeof(AIS_Message) + len + 1); memcpy(ais_msg->data, compressed, len); ais_msg->data[len] = 0; crm_free(compressed); ais_msg->is_compressed = TRUE; ais_msg->compressed_size = len; crm_debug_2("Compression details: %d -> %d", ais_msg->size, ais_data_len(ais_msg)); } ais_msg->header.size = sizeof(AIS_Message) + ais_data_len(ais_msg); crm_debug_3("Sending%s message %d to %s.%s (data=%d, total=%d)", ais_msg->is_compressed?" compressed":"", ais_msg->id, ais_dest(&(ais_msg->host)), msg_type2text(dest), ais_data_len(ais_msg), ais_msg->header.size); iov.iov_base = ais_msg; iov.iov_len = ais_msg->header.size; crm_realloc(buf, buf_len); do { if(rc == CS_ERR_TRY_AGAIN) { retries++; crm_info("Peer overloaded or membership in flux:" " Re-sending message (Attempt %d of 20)", retries); sleep(retries); /* Proportional back off */ } errno = 0; switch(cluster_type) { case pcmk_cluster_classic_ais: rc = coroipcc_msg_send_reply_receive(ais_ipc_handle, &iov, 1, buf, buf_len); header = (coroipc_response_header_t *)buf; if(rc == CS_OK) { CRM_CHECK(header->size == sizeof (coroipc_response_header_t), crm_err("Odd message: id=%d, size=%d, class=%d, error=%d", header->id, header->size, class, header->error)); CRM_ASSERT(buf_len >= header->size); CRM_CHECK(header->id == CRM_MESSAGE_IPC_ACK, crm_err("Bad response id (%d) for request (%d)", header->id, ais_msg->header.id)); CRM_CHECK(header->error == CS_OK, rc = header->error); } break; case pcmk_cluster_corosync: case pcmk_cluster_cman: transport = "cpg"; CRM_CHECK(dest != crm_msg_ais, rc = CS_ERR_MESSAGE_ERROR; goto bail); rc = cpg_mcast_joined(pcmk_cpg_handle, CPG_TYPE_AGREED, &iov, 1); if(rc == CS_ERR_TRY_AGAIN) { cpg_flow_control_state_t fc_state = CPG_FLOW_CONTROL_DISABLED; int rc2 = cpg_flow_control_state_get (pcmk_cpg_handle, &fc_state); if (rc2 == CS_OK && fc_state == CPG_FLOW_CONTROL_ENABLED) { crm_warn("Connection overloaded, cannot send messages"); goto bail; } else if (rc2 != CS_OK) { crm_warn("Could not determin the connection state: %s (%d)", ais_error2text(rc2), rc2); goto bail; } } break; case pcmk_cluster_unknown: case pcmk_cluster_invalid: case pcmk_cluster_heartbeat: CRM_ASSERT(is_openais_cluster()); break; } } while (rc == CS_ERR_TRY_AGAIN && retries < 20); bail: if(rc != CS_OK) { crm_perror(LOG_ERR,"Sending message %d via %s: FAILED (rc=%d): %s", ais_msg->id, transport, rc, ais_error2text(rc)); } else { crm_debug_4("Message %d: sent", ais_msg->id); } crm_free(buf); crm_free(ais_msg); return (rc == CS_OK); } gboolean send_ais_message(xmlNode *msg, gboolean local, const char *node, enum crm_ais_msg_types dest) { gboolean rc = TRUE; char *data = NULL; if(is_classic_ais_cluster()) { if(ais_fd_async < 0 || ais_source == NULL) { crm_err("Not connected to AIS: %d %p", ais_fd_async, ais_source); return FALSE; } } data = dump_xml_unformatted(msg); rc = send_ais_text(0, data, local, node, dest); crm_free(data); return rc; } void terminate_ais_connection(void) { crm_notice("Disconnecting from AIS"); /* G_main_del_fd(ais_source); */ /* G_main_del_fd(ais_source_sync); */ #ifdef SUPPORT_CMAN if(is_cman_cluster()) { cman_stop_notification(pcmk_cman_handle); cman_finish(pcmk_cman_handle); } #endif if(is_corosync_cluster()) { quorum_finalize(pcmk_quorum_handle); } if(is_classic_ais_cluster() == FALSE) { coroipcc_service_disconnect(ais_ipc_handle); } else { cpg_leave(pcmk_cpg_handle, &pcmk_cpg_group); } } int ais_membership_timer = 0; gboolean ais_membership_force = FALSE; static gboolean ais_dispatch_message( AIS_Message *msg, gboolean (*dispatch)(AIS_Message*,char*,int)) { char *data = NULL; char *uncompressed = NULL; xmlNode *xml = NULL; CRM_ASSERT(msg != NULL); crm_debug_3("Got new%s message (size=%d, %d, %d)", msg->is_compressed?" compressed":"", ais_data_len(msg), msg->size, msg->compressed_size); data = msg->data; if(msg->is_compressed && msg->size > 0) { int rc = BZ_OK; unsigned int new_size = msg->size + 1; if(check_message_sanity(msg, NULL) == FALSE) { goto badmsg; } crm_debug_5("Decompressing message data"); crm_malloc0(uncompressed, new_size); rc = BZ2_bzBuffToBuffDecompress( uncompressed, &new_size, data, msg->compressed_size, 1, 0); if(rc != BZ_OK) { crm_err("Decompression failed: %d", rc); goto badmsg; } CRM_ASSERT(rc == BZ_OK); CRM_ASSERT(new_size == msg->size); data = uncompressed; } else if(check_message_sanity(msg, data) == FALSE) { goto badmsg; } else if(safe_str_eq("identify", data)) { int pid = getpid(); char *pid_s = crm_itoa(pid); send_ais_text(0, pid_s, TRUE, NULL, crm_msg_ais); crm_free(pid_s); goto done; } if(msg->header.id != crm_class_members) { crm_update_peer(msg->sender.id, 0,0,0,0, msg->sender.uname, msg->sender.uname, NULL, NULL); } if(msg->header.id == crm_class_rmpeer) { uint32_t id = crm_int_helper(data, NULL); crm_info("Removing peer %s/%u", data, id); reap_crm_member(id); goto done; } else if(msg->header.id == crm_class_members || msg->header.id == crm_class_quorum) { xml = string2xml(data); if(xml == NULL) { crm_err("Invalid membership update: %s", data); goto badmsg; } if(is_classic_ais_cluster() == FALSE) { - xml_child_iter(xml, node, crm_update_cman_node(node, crm_peer_seq)); - + xmlNode *node = NULL; + for(node = xml; node != NULL; node = node->next) { + crm_update_cman_node(node, crm_peer_seq); + } + } else { + xmlNode *node = NULL; const char *value = NULL; gboolean quorate = FALSE; value = crm_element_value(xml, "quorate"); CRM_CHECK(value != NULL, crm_log_xml_err(xml, "No quorum value:"); goto badmsg); if(crm_is_true(value)) { quorate = TRUE; } value = crm_element_value(xml, "id"); CRM_CHECK(value != NULL, crm_log_xml_err(xml, "No membership id"); goto badmsg); crm_peer_seq = crm_int_helper(value, NULL); if(quorate != crm_have_quorum) { crm_notice("Membership %s: quorum %s", value, quorate?"acquired":"lost"); crm_have_quorum = quorate; } else { crm_info("Membership %s: quorum %s", value, quorate?"retained":"still lost"); } - xml_child_iter(xml, node, crm_update_ais_node(node, crm_peer_seq)); + for(node = xml; node != NULL; node = node->next) { + crm_update_ais_node(node, crm_peer_seq); + } } } if(dispatch != NULL) { dispatch(msg, data, 0); } done: crm_free(uncompressed); free_xml(xml); return TRUE; badmsg: crm_err("Invalid message (id=%d, dest=%s:%s, from=%s:%s.%d):" " min=%d, total=%d, size=%d, bz2_size=%d", msg->id, ais_dest(&(msg->host)), msg_type2text(msg->host.type), ais_dest(&(msg->sender)), msg_type2text(msg->sender.type), msg->sender.pid, (int)sizeof(AIS_Message), msg->header.size, msg->size, msg->compressed_size); goto done; } gboolean ais_dispatch(int sender, gpointer user_data) { int rc = CS_OK; char *buffer = NULL; gboolean good = TRUE; gboolean (*dispatch)(AIS_Message*,char*,int) = user_data; rc = coroipcc_dispatch_get (ais_ipc_handle, (void**)&buffer, 0); if (rc == 0 || buffer == NULL) { /* Zero is a legal "no message afterall" value */ return TRUE; } else if (rc != CS_OK) { crm_perror(LOG_ERR,"Receiving message body failed: (%d) %s", rc, ais_error2text(rc)); goto bail; } good = ais_dispatch_message((AIS_Message*)buffer, dispatch); coroipcc_dispatch_put (ais_ipc_handle); return good; bail: crm_err("AIS connection failed"); return FALSE; } static void ais_destroy(gpointer user_data) { crm_err("AIS connection terminated"); ais_fd_sync = -1; exit(1); } static gboolean pcmk_proc_dispatch(IPC_Channel *ch, gpointer user_data) { xmlNode *msg = NULL; gboolean stay_connected = TRUE; while(IPC_ISRCONN(ch)) { if(ch->ops->is_message_pending(ch) == 0) { break; } msg = xmlfromIPC(ch, MAX_IPC_DELAY); if(msg) { - xml_child_iter(msg, node, - - int id = 0; - int children = 0; - const char *uname = crm_element_value(node, "uname"); - crm_element_value_int(node, "processes", &children); - - crm_update_peer(id, 0, 0, 0, children, NULL, uname, NULL, NULL); - ); + xmlNode *node = NULL; + for(node = msg; node != NULL; node = node->next) { + int id = 0; + int children = 0; + const char *uname = crm_element_value(node, "uname"); + crm_element_value_int(node, "processes", &children); + + crm_update_peer(id, 0, 0, 0, children, NULL, uname, NULL, NULL); + } free_xml(msg); } if(ch->ch_status != IPC_CONNECT) { break; } } if (ch->ch_status != IPC_CONNECT) { stay_connected = FALSE; } return stay_connected; } #ifdef SUPPORT_CMAN static gboolean pcmk_cman_dispatch(int sender, gpointer user_data) { int rc = cman_dispatch(pcmk_cman_handle, CMAN_DISPATCH_ONE); if(rc < 0) { crm_err("Connection to cman failed: %d", rc); return FALSE; } return TRUE; } #define MAX_NODES 256 static void cman_event_callback(cman_handle_t handle, void *privdata, int reason, int arg) { int rc = 0, lpc = 0, node_count = 0; cman_cluster_t cluster; static cman_node_t cman_nodes[MAX_NODES]; gboolean (*dispatch)(unsigned long long, gboolean) = privdata; switch (reason) { case CMAN_REASON_STATECHANGE: memset(&cluster, 0, sizeof(cluster)); rc = cman_get_cluster(pcmk_cman_handle, &cluster); if (rc < 0) { crm_err("Couldn't query cman cluster details: %d %d", rc, errno); return; } crm_peer_seq = cluster.ci_generation; if(arg != crm_have_quorum) { crm_notice("Membership %llu: quorum %s", crm_peer_seq, arg?"acquired":"lost"); crm_have_quorum = arg; } else { crm_info("Membership %llu: quorum %s", crm_peer_seq, arg?"retained":"still lost"); } rc = cman_get_nodes(pcmk_cman_handle, MAX_NODES, &node_count, cman_nodes); if (rc < 0) { crm_err("Couldn't query cman node list: %d %d", rc, errno); return; } for (lpc = 0; lpc < node_count; lpc++) { if (cman_nodes[lpc].cn_nodeid == 0) { /* Never allow node ID 0 to be considered a member #315711 */ cman_nodes[lpc].cn_member = 0; } crm_update_peer(cman_nodes[lpc].cn_nodeid, cman_nodes[lpc].cn_incarnation, cman_nodes[lpc].cn_member?crm_peer_seq:0, 0, 0, cman_nodes[lpc].cn_name, cman_nodes[lpc].cn_name, NULL, cman_nodes[lpc].cn_member?CRM_NODE_MEMBER:CRM_NODE_LOST); } if(dispatch) { dispatch(crm_peer_seq, crm_have_quorum); } break; case CMAN_REASON_TRY_SHUTDOWN: /* Always reply with a negative - pacemaker needs to be stopped first */ crm_info("CMAN wants to shut down: %s", arg?"forced":"optional"); cman_replyto_shutdown(pcmk_cman_handle, 0); break; case CMAN_REASON_CONFIG_UPDATE: /* Ignore */ break; } } #endif gboolean init_cman_connection( gboolean (*dispatch)(unsigned long long, gboolean), void (*destroy)(gpointer)) { #ifdef SUPPORT_CMAN int rc = -1, fd = -1; cman_cluster_t cluster; crm_info("Configuring Pacemaker to obtain quorum from cman"); memset(&cluster, 0, sizeof(cluster)); pcmk_cman_handle = cman_init(dispatch); if(pcmk_cman_handle == NULL || cman_is_active(pcmk_cman_handle) == FALSE) { crm_err("Couldn't connect to cman"); goto cman_bail; } rc = cman_get_cluster(pcmk_cman_handle, &cluster); if (rc < 0) { crm_err("Couldn't query cman cluster details: %d %d", rc, errno); goto cman_bail; } ais_cluster_name = crm_strdup(cluster.ci_name); rc = cman_start_notification(pcmk_cman_handle, cman_event_callback); if (rc < 0) { crm_err("Couldn't register for cman notifications: %d %d", rc, errno); goto cman_bail; } /* Get the current membership state */ cman_event_callback(pcmk_cman_handle, dispatch, CMAN_REASON_STATECHANGE, cman_is_quorate(pcmk_cman_handle)); fd = cman_get_fd(pcmk_cman_handle); crm_debug("Adding fd=%d to mainloop", fd); cman_source = G_main_add_fd( G_PRIORITY_HIGH, fd, FALSE, pcmk_cman_dispatch, dispatch, destroy); cman_bail: if (rc < 0) { cman_finish(pcmk_cman_handle); return FALSE; } #else crm_err("cman qorum is not supported in this build"); exit(100); #endif return TRUE; } #ifdef SUPPORT_CS_QUORUM gboolean (*pcmk_cpg_dispatch_fn)(AIS_Message*,char*,int) = NULL; static gboolean pcmk_cpg_dispatch(int sender, gpointer user_data) { int rc = 0; pcmk_cpg_dispatch_fn = user_data; rc = cpg_dispatch(pcmk_cpg_handle, CS_DISPATCH_ALL); if(rc != CS_OK) { crm_err("Connection to the CPG API failed: %d", rc); return FALSE; } return TRUE; } static void pcmk_cpg_deliver ( cpg_handle_t handle, const struct cpg_name *groupName, uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len) { AIS_Message *ais_msg = (AIS_Message*)msg; if(ais_msg->sender.id > 0 && ais_msg->sender.id != nodeid) { crm_err("Nodeid mismatch from %d.%d: claimed nodeid=%u", nodeid, pid, ais_msg->sender.id); return; } else if(ais_msg->host.size != 0 && safe_str_neq(ais_msg->host.uname, pcmk_uname)) { /* Not for us */ return; } ais_msg->sender.id = nodeid; if(ais_msg->sender.size == 0) { crm_node_t *peer = crm_get_peer(nodeid, NULL); if(peer == NULL) { crm_err("Peer with nodeid=%u is unknown", nodeid); } else if(peer->uname == NULL) { crm_err("No uname for peer with nodeid=%u", nodeid); } else { crm_notice("Fixing uname for peer with nodeid=%u", nodeid); ais_msg->sender.size = strlen(peer->uname); memset(ais_msg->sender.uname, 0, MAX_NAME); memcpy(ais_msg->sender.uname, peer->uname, ais_msg->sender.size); } } ais_dispatch_message(ais_msg, pcmk_cpg_dispatch_fn); } static void pcmk_cpg_membership( cpg_handle_t handle, const struct cpg_name *groupName, const struct cpg_address *member_list, size_t member_list_entries, const struct cpg_address *left_list, size_t left_list_entries, const struct cpg_address *joined_list, size_t joined_list_entries) { /* Don't care about CPG membership */ } static gboolean pcmk_quorum_dispatch(int sender, gpointer user_data) { int rc = 0; rc = quorum_dispatch(pcmk_quorum_handle, CS_DISPATCH_ALL); if(rc < 0) { crm_err("Connection to the Quorum API failed: %d", rc); return FALSE; } return TRUE; } static void pcmk_quorum_notification( quorum_handle_t handle, uint32_t quorate, uint64_t ring_id, uint32_t view_list_entries, uint32_t *view_list) { int i; if(quorate != crm_have_quorum) { crm_notice("Membership "U64T": quorum %s (%lu)", ring_id, quorate?"acquired":"lost", (long unsigned int)view_list_entries); crm_have_quorum = quorate; } else { crm_info("Membership "U64T": quorum %s (%lu)", ring_id, quorate?"retained":"still lost", (long unsigned int)view_list_entries); } for (i=0; ihost.type; int tmp_size = msg->header.size - sizeof(AIS_Message); if(sane && msg->header.size == 0) { crm_warn("Message with no size"); sane = FALSE; } if(sane && msg->header.error != CS_OK) { crm_warn("Message header contains an error: %d", msg->header.error); sane = FALSE; } if(sane && ais_data_len(msg) != tmp_size) { crm_warn("Message payload size is incorrect: expected %d, got %d", ais_data_len(msg), tmp_size); sane = TRUE; } if(sane && ais_data_len(msg) == 0) { crm_warn("Message with no payload"); sane = FALSE; } if(sane && data && msg->is_compressed == FALSE) { int str_size = strlen(data) + 1; if(ais_data_len(msg) != str_size) { int lpc = 0; crm_warn("Message payload is corrupted: expected %d bytes, got %d", ais_data_len(msg), str_size); sane = FALSE; for(lpc = (str_size - 10); lpc < msg->size; lpc++) { if(lpc < 0) { lpc = 0; } crm_debug("bad_data[%d]: %d / '%c'", lpc, data[lpc], data[lpc]); } } } if(sane == FALSE) { crm_err("Invalid message %d: (dest=%s:%s, from=%s:%s.%d, compressed=%d, size=%d, total=%d)", msg->id, ais_dest(&(msg->host)), msg_type2text(dest), ais_dest(&(msg->sender)), msg_type2text(msg->sender.type), msg->sender.pid, msg->is_compressed, ais_data_len(msg), msg->header.size); } else if(repaired) { crm_err("Repaired message %d: (dest=%s:%s, from=%s:%s.%d, compressed=%d, size=%d, total=%d)", msg->id, ais_dest(&(msg->host)), msg_type2text(dest), ais_dest(&(msg->sender)), msg_type2text(msg->sender.type), msg->sender.pid, msg->is_compressed, ais_data_len(msg), msg->header.size); } else { crm_debug_3("Verfied message %d: (dest=%s:%s, from=%s:%s.%d, compressed=%d, size=%d, total=%d)", msg->id, ais_dest(&(msg->host)), msg_type2text(dest), ais_dest(&(msg->sender)), msg_type2text(msg->sender.type), msg->sender.pid, msg->is_compressed, ais_data_len(msg), msg->header.size); } return sane; } #endif diff --git a/lib/common/xml.c b/lib/common/xml.c index 6fc1ee9745..285203d180 100644 --- a/lib/common/xml.c +++ b/lib/common/xml.c @@ -1,3017 +1,3043 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_BZLIB_H # include #endif #define XML_BUFFER_SIZE 4096 #define XML_PARSER_DEBUG 0 #define BEST_EFFORT_STATUS 0 xmlDoc *getDocPtr(xmlNode *node); typedef struct { - xmlRelaxNGPtr rng; - xmlRelaxNGValidCtxtPtr valid; - xmlRelaxNGParserCtxtPtr parser; + xmlRelaxNGPtr rng; + xmlRelaxNGValidCtxtPtr valid; + xmlRelaxNGParserCtxtPtr parser; } relaxng_ctx_cache_t; struct schema_s { int type; const char *name; const char *location; const char *transform; int after_transform; void *cache; }; struct schema_s known_schemas[] = { -/* 0 */ { 0, NULL, NULL, NULL, 1 }, -/* 1 */ { 1, "pacemaker-0.6", CRM_DTD_DIRECTORY"/crm.dtd", CRM_DTD_DIRECTORY"/upgrade06.xsl", 4, NULL }, -/* 2 */ { 1, "transitional-0.6", CRM_DTD_DIRECTORY"/crm-transitional.dtd", CRM_DTD_DIRECTORY"/upgrade06.xsl", 4, NULL }, -/* 3 */ { 2, "pacemaker-0.7", CRM_DTD_DIRECTORY"/pacemaker-1.0.rng", NULL, 0, NULL }, -/* 4 */ { 2, "pacemaker-1.0", CRM_DTD_DIRECTORY"/pacemaker-1.0.rng", NULL, 6, NULL }, -/* 5 */ { 2, "pacemaker-1.1", CRM_DTD_DIRECTORY"/pacemaker-1.1.rng", NULL, 6, NULL }, -/* 6 */ { 2, "pacemaker-1.2", CRM_DTD_DIRECTORY"/pacemaker-1.2.rng", NULL, 0, NULL }, -/* 7 */ { 0, "none", NULL, NULL, 0, NULL }, + /* 0 */ { 0, NULL, NULL, NULL, 1 }, + /* 1 */ { 1, "pacemaker-0.6", CRM_DTD_DIRECTORY"/crm.dtd", CRM_DTD_DIRECTORY"/upgrade06.xsl", 4, NULL }, + /* 2 */ { 1, "transitional-0.6", CRM_DTD_DIRECTORY"/crm-transitional.dtd", CRM_DTD_DIRECTORY"/upgrade06.xsl", 4, NULL }, + /* 3 */ { 2, "pacemaker-0.7", CRM_DTD_DIRECTORY"/pacemaker-1.0.rng", NULL, 0, NULL }, + /* 4 */ { 2, "pacemaker-1.0", CRM_DTD_DIRECTORY"/pacemaker-1.0.rng", NULL, 6, NULL }, + /* 5 */ { 2, "pacemaker-1.1", CRM_DTD_DIRECTORY"/pacemaker-1.1.rng", NULL, 6, NULL }, + /* 6 */ { 2, "pacemaker-1.2", CRM_DTD_DIRECTORY"/pacemaker-1.2.rng", NULL, 0, NULL }, + /* 7 */ { 0, "none", NULL, NULL, 0, NULL }, }; static int all_schemas = DIMOF(known_schemas); static int max_schemas = DIMOF(known_schemas) - 2; /* skip back past 'none' */ -typedef struct -{ - int found; - const char *string; -} filter_t; + typedef struct + { + int found; + const char *string; + } filter_t; static filter_t filter[] = { { 0, XML_ATTR_ORIGIN }, { 0, XML_CIB_ATTR_WRITTEN }, }; static void add_ha_nocopy(HA_Message *parent, HA_Message *child, const char *field) { int next = parent->nfields; if (parent->nfields >= parent->nalloc && ha_msg_expand(parent) != HA_OK ) { crm_err("Parent expansion failed"); return; } parent->names[next] = crm_strdup(field); parent->nlens[next] = strlen(field); parent->values[next] = child; parent->vlens[next] = sizeof(HA_Message); parent->types[next] = FT_UNCOMPRESS; parent->nfields++; } int print_spaces(char *buffer, int spaces, int max); int get_tag_name(const char *input, size_t offset, size_t max); int get_attr_name(const char *input, size_t offset, size_t max); int get_attr_value(const char *input, size_t offset, size_t max); gboolean can_prune_leaf(xmlNode *xml_node); void diff_filter_context(int context, int upper_bound, int lower_bound, - xmlNode *xml_node, xmlNode *parent); + xmlNode *xml_node, xmlNode *parent); int in_upper_context(int depth, int context, xmlNode *xml_node); int write_file(const char *string, const char *filename); xmlNode *subtract_xml_object(xmlNode *parent, xmlNode *left, xmlNode *right, gboolean full, const char *marker); int add_xml_object(xmlNode *parent, xmlNode *target, xmlNode *update, gboolean as_diff); xmlNode * find_xml_node(xmlNode *root, const char * search_path, gboolean must_find) { - const char *name = "NULL"; - if(must_find || root != NULL) { - crm_validate_data(root); - } - if(root != NULL) { - name = crm_element_name(root); - } + xmlNode *a_child = NULL; + const char *name = "NULL"; + if(must_find || root != NULL) { + crm_validate_data(root); + } + if(root != NULL) { + name = crm_element_name(root); + } - if(search_path == NULL) { - crm_warn("Will never find "); - return NULL; - } + if(search_path == NULL) { + crm_warn("Will never find "); + return NULL; + } - xml_child_iter_filter( - root, a_child, search_path, + for(a_child = root; a_child != NULL; a_child = a_child->next) { + if(crm_str_eq((const char *)a_child->name, search_path, TRUE)) { /* crm_debug_5("returning node (%s).", crm_element_name(a_child)); */ - crm_validate_data(a_child); - return a_child; - ); - - if(must_find) { - crm_warn("Could not find %s in %s.", search_path, name); - } else if(root != NULL) { - crm_debug_3("Could not find %s in %s.", search_path, name); - } else { - crm_debug_3("Could not find %s in .", search_path); + crm_validate_data(a_child); + return a_child; } + } + + if(must_find) { + crm_warn("Could not find %s in %s.", search_path, name); + } else if(root != NULL) { + crm_debug_3("Could not find %s in %s.", search_path, name); + } else { + crm_debug_3("Could not find %s in .", search_path); + } - return NULL; + return NULL; } xmlNode* find_entity(xmlNode *parent, const char *node_name, const char *id) { - crm_validate_data(parent); - xml_child_iter_filter( - parent, a_child, node_name, - if(id == NULL || crm_str_eq(id, ID(a_child), TRUE)) { - crm_debug_4("returning node (%s).", - crm_element_name(a_child)); - return a_child; - } - ); - crm_debug_3("node <%s id=%s> not found in %s.", - node_name, id, crm_element_name(parent)); - return NULL; + xmlNode *a_child = NULL; + crm_validate_data(parent); + for(a_child = parent; a_child != NULL; a_child = a_child->next) { + if(crm_str_eq((const char *)a_child->name, node_name, TRUE)) { + if(id == NULL || crm_str_eq(id, ID(a_child), TRUE)) { + crm_debug_4("returning node (%s).", + crm_element_name(a_child)); + return a_child; + } + } + } + + crm_debug_3("node <%s id=%s> not found in %s.", + node_name, id, crm_element_name(parent)); + return NULL; } void copy_in_properties(xmlNode* target, xmlNode *src) { - crm_validate_data(src); - crm_validate_data(target); + crm_validate_data(src); + crm_validate_data(target); - if(src == NULL) { - crm_warn("No node to copy properties from"); + if(src == NULL) { + crm_warn("No node to copy properties from"); - } else if (target == NULL) { - crm_err("No node to copy properties into"); + } else if (target == NULL) { + crm_err("No node to copy properties into"); - } else { - xml_prop_iter( - src, local_prop_name, local_prop_value, - expand_plus_plus(target, local_prop_name, local_prop_value) - ); - crm_validate_data(target); - } + } else { + xml_prop_iter( + src, local_prop_name, local_prop_value, + expand_plus_plus(target, local_prop_name, local_prop_value) + ); + crm_validate_data(target); + } - return; + return; } void fix_plus_plus_recursive(xmlNode* target) { /* TODO: Remove recursion and use xpath searches for value++ */ + xmlNode *child = NULL; xml_prop_iter(target, name, value, expand_plus_plus(target, name, value)); - xml_child_iter(target, child, fix_plus_plus_recursive(child)); + for(child = target; child != NULL; child = child->next) { + fix_plus_plus_recursive(child); + } } void expand_plus_plus(xmlNode* target, const char *name, const char *value) { int offset = 1; int name_len = 0; int int_value = 0; int value_len = 0; const char *old_value = NULL; if(value == NULL || name == NULL) { return; } old_value = crm_element_value(target, name); if(old_value == NULL) { /* if no previous value, set unexpanded */ goto set_unexpanded; } else if(strstr(value, name) != value) { goto set_unexpanded; } name_len = strlen(name); value_len = strlen(value); if(value_len < (name_len + 2) - || value[name_len] != '+' - || (value[name_len+1] != '+' && value[name_len+1] != '=')) { + || value[name_len] != '+' + || (value[name_len+1] != '+' && value[name_len+1] != '=')) { goto set_unexpanded; } /* if we are expanding ourselves, * then no previous value was set and leave int_value as 0 */ if(old_value != value) { int_value = char2score(old_value); } if(value[name_len+1] != '+') { const char *offset_s = value+(name_len+2); offset = char2score(offset_s); } int_value += offset; if(int_value > INFINITY) { int_value = INFINITY; } crm_xml_add_int(target, name, int_value); return; set_unexpanded: if(old_value == value) { /* the old value is already set, nothing to do */ return; } crm_xml_add(target, name, value); return; } xmlDoc *getDocPtr(xmlNode *node) { xmlDoc *doc = NULL; CRM_CHECK(node != NULL, return NULL); doc = node->doc; if(doc == NULL) { doc = xmlNewDoc((const xmlChar*)"1.0"); xmlDocSetRootElement(doc, node); xmlSetTreeDoc(node, doc); } return doc; } xmlNode* add_node_copy(xmlNode *parent, xmlNode *src_node) { - xmlNode *child = NULL; - xmlDoc *doc = getDocPtr(parent); - CRM_CHECK(src_node != NULL, return NULL); + xmlNode *child = NULL; + xmlDoc *doc = getDocPtr(parent); + CRM_CHECK(src_node != NULL, return NULL); - child = xmlDocCopyNode(src_node, doc, 1); - xmlAddChild(parent, child); - return child; + child = xmlDocCopyNode(src_node, doc, 1); + xmlAddChild(parent, child); + return child; } int add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child) { - add_node_copy(parent, child); - free_xml(child); - return HA_OK; + add_node_copy(parent, child); + free_xml(child); + return HA_OK; } const char * crm_xml_add(xmlNode* node, const char *name, const char *value) { xmlAttr *attr = NULL; CRM_CHECK(node != NULL, return NULL); CRM_CHECK(name != NULL, return NULL); if(value == NULL) { return NULL; } #if XML_PARANOIA_CHECKS { const char *old_value = NULL; old_value = crm_element_value(node, name); /* Could be re-setting the same value */ CRM_CHECK(old_value != value, - crm_err("Cannot reset %s with crm_xml_add(%s)", - name, value); - return value); + crm_err("Cannot reset %s with crm_xml_add(%s)", + name, value); + return value); } #endif attr = xmlSetProp(node, (const xmlChar*)name, (const xmlChar*)value); CRM_CHECK(attr && attr->children && attr->children->content, return NULL); return (char *)attr->children->content; } const char * crm_xml_replace(xmlNode* node, const char *name, const char *value) { xmlAttr *attr = NULL; const char *old_value = NULL; CRM_CHECK(node != NULL, return NULL); CRM_CHECK(name != NULL && name[0] != 0, return NULL); old_value = crm_element_value(node, name); /* Could be re-setting the same value */ CRM_CHECK(old_value != value, return value); if (old_value != NULL && value == NULL) { xml_remove_prop(node, name); return NULL; } else if(value == NULL) { return NULL; } attr = xmlSetProp(node, (const xmlChar*)name, (const xmlChar*)value); CRM_CHECK(attr && attr->children && attr->children->content, return NULL); return (char *)attr->children->content; } const char * crm_xml_add_int(xmlNode* node, const char *name, int value) { char *number = crm_itoa(value); const char *added = crm_xml_add(node, name, number); crm_free(number); return added; } xmlNode* create_xml_node(xmlNode *parent, const char *name) { xmlDoc *doc = NULL; xmlNode *node = NULL; if (name == NULL || name[0] == 0) { return NULL; } if(parent == NULL) { doc = xmlNewDoc((const xmlChar*)"1.0"); node = xmlNewDocRawNode(doc, NULL, (const xmlChar*)name, NULL); xmlDocSetRootElement(doc, node); } else { doc = getDocPtr(parent); node = xmlNewDocRawNode(doc, NULL, (const xmlChar*)name, NULL); xmlAddChild(parent, node); } return node; } void free_xml_from_parent(xmlNode *parent, xmlNode *a_node) { - CRM_CHECK(a_node != NULL, return); + CRM_CHECK(a_node != NULL, return); - xmlUnlinkNode(a_node); - xmlFreeNode(a_node); + xmlUnlinkNode(a_node); + xmlFreeNode(a_node); } xmlNode* copy_xml(xmlNode *src) { xmlDoc *doc = xmlNewDoc((const xmlChar*)"1.0"); xmlNode *copy = xmlDocCopyNode(src, doc, 1); xmlDocSetRootElement(doc, copy); xmlSetTreeDoc(copy, doc); return copy; } static void crm_xml_err(void * ctx, const char * msg, ...) G_GNUC_PRINTF(2,3); extern size_t strlcat(char * dest, const char *source, size_t len); int write_file(const char *string, const char *filename) { - int rc = 0; - FILE *file_output_strm = NULL; + int rc = 0; + FILE *file_output_strm = NULL; - CRM_CHECK(filename != NULL, return -1); + CRM_CHECK(filename != NULL, return -1); - if (string == NULL) { - crm_err("Cannot write NULL to %s", filename); - return -1; - } + if (string == NULL) { + crm_err("Cannot write NULL to %s", filename); + return -1; + } - file_output_strm = fopen(filename, "w"); - if(file_output_strm == NULL) { - crm_perror(LOG_ERR,"Cannot open %s for writing", filename); - return -1; - } + file_output_strm = fopen(filename, "w"); + if(file_output_strm == NULL) { + crm_perror(LOG_ERR,"Cannot open %s for writing", filename); + return -1; + } - rc = fprintf(file_output_strm, "%s", string); - if(rc < 0) { - crm_perror(LOG_ERR,"Cannot write output to %s", filename); - } + rc = fprintf(file_output_strm, "%s", string); + if(rc < 0) { + crm_perror(LOG_ERR,"Cannot write output to %s", filename); + } - if(fflush(file_output_strm) != 0) { - crm_perror(LOG_ERR,"fflush for %s failed:", filename); - rc = -1; - } + if(fflush(file_output_strm) != 0) { + crm_perror(LOG_ERR,"fflush for %s failed:", filename); + rc = -1; + } - if(fsync(fileno(file_output_strm)) < 0) { - crm_perror(LOG_ERR,"fsync for %s failed:", filename); - rc = -1; - } + if(fsync(fileno(file_output_strm)) < 0) { + crm_perror(LOG_ERR,"fsync for %s failed:", filename); + rc = -1; + } - fclose(file_output_strm); - return rc; + fclose(file_output_strm); + return rc; } static void crm_xml_err(void * ctx, const char * msg, ...) { int len = 0; va_list args; char *buf = NULL; static int buffer_len = 0; static char *buffer = NULL; va_start(args, msg); len = vasprintf(&buf, msg, args); if(strchr(buf, '\n')) { buf[len - 1] = 0; if(buffer) { crm_err("XML Error: %s%s", buffer, buf); free(buffer); } else { crm_err("XML Error: %s", buf); } buffer = NULL; buffer_len = 0; } else if(buffer == NULL) { buffer_len = len; buffer = buf; buf = NULL; } else { buffer_len += len; buffer = realloc(buffer, buffer_len); strlcat(buffer, buf, buffer_len); } va_end(args); free(buf); } xmlNode* string2xml(const char *input) { - xmlNode *xml = NULL; - xmlDocPtr output = NULL; - xmlParserCtxtPtr ctxt = NULL; - xmlErrorPtr last_error = NULL; + xmlNode *xml = NULL; + xmlDocPtr output = NULL; + xmlParserCtxtPtr ctxt = NULL; + xmlErrorPtr last_error = NULL; - if(input == NULL) { - crm_err("Can't parse NULL input"); - return NULL; - } + if(input == NULL) { + crm_err("Can't parse NULL input"); + return NULL; + } - /* create a parser context */ - ctxt = xmlNewParserCtxt(); - CRM_CHECK(ctxt != NULL, return NULL); + /* create a parser context */ + ctxt = xmlNewParserCtxt(); + CRM_CHECK(ctxt != NULL, return NULL); - /* xmlCtxtUseOptions(ctxt, XML_PARSE_NOBLANKS|XML_PARSE_RECOVER); */ + /* xmlCtxtUseOptions(ctxt, XML_PARSE_NOBLANKS|XML_PARSE_RECOVER); */ - xmlCtxtResetLastError(ctxt); - xmlSetGenericErrorFunc(ctxt, crm_xml_err); - /* initGenericErrorDefaultFunc(crm_xml_err); */ - output = xmlCtxtReadDoc(ctxt, (const xmlChar*)input, NULL, NULL, XML_PARSE_NOBLANKS|XML_PARSE_RECOVER); - if(output) { - xml = xmlDocGetRootElement(output); - } - last_error = xmlCtxtGetLastError(ctxt); - if(last_error && last_error->code != XML_ERR_OK) { - /* crm_abort(__FILE__,__PRETTY_FUNCTION__,__LINE__, "last_error->code != XML_ERR_OK", TRUE, TRUE); */ - /* - * http://xmlsoft.org/html/libxml-xmlerror.html#xmlErrorLevel - * http://xmlsoft.org/html/libxml-xmlerror.html#xmlParserErrors - */ - crm_warn("Parsing failed (domain=%d, level=%d, code=%d): %s", - last_error->domain, last_error->level, - last_error->code, last_error->message); - - if(last_error->code != XML_ERR_DOCUMENT_END) { - crm_err("Couldn't%s parse %d chars: %s", xml?" fully":"", (int)strlen(input), input); - if(xml != NULL) { - crm_log_xml_err(xml, "Partial"); - } + xmlCtxtResetLastError(ctxt); + xmlSetGenericErrorFunc(ctxt, crm_xml_err); + /* initGenericErrorDefaultFunc(crm_xml_err); */ + output = xmlCtxtReadDoc(ctxt, (const xmlChar*)input, NULL, NULL, XML_PARSE_NOBLANKS|XML_PARSE_RECOVER); + if(output) { + xml = xmlDocGetRootElement(output); + } + last_error = xmlCtxtGetLastError(ctxt); + if(last_error && last_error->code != XML_ERR_OK) { + /* crm_abort(__FILE__,__PRETTY_FUNCTION__,__LINE__, "last_error->code != XML_ERR_OK", TRUE, TRUE); */ + /* + * http://xmlsoft.org/html/libxml-xmlerror.html#xmlErrorLevel + * http://xmlsoft.org/html/libxml-xmlerror.html#xmlParserErrors + */ + crm_warn("Parsing failed (domain=%d, level=%d, code=%d): %s", + last_error->domain, last_error->level, + last_error->code, last_error->message); - } else { - int len = strlen(input); - crm_warn("String start: %.50s", input); - crm_warn("String start+%d: %s", len-50, input+len-50); - crm_abort(__FILE__,__PRETTY_FUNCTION__,__LINE__, "String parsing error", TRUE, TRUE); + if(last_error->code != XML_ERR_DOCUMENT_END) { + crm_err("Couldn't%s parse %d chars: %s", xml?" fully":"", (int)strlen(input), input); + if(xml != NULL) { + crm_log_xml_err(xml, "Partial"); } + + } else { + int len = strlen(input); + crm_warn("String start: %.50s", input); + crm_warn("String start+%d: %s", len-50, input+len-50); + crm_abort(__FILE__,__PRETTY_FUNCTION__,__LINE__, "String parsing error", TRUE, TRUE); } + } - xmlFreeParserCtxt(ctxt); - return xml; + xmlFreeParserCtxt(ctxt); + return xml; } xmlNode * stdin2xml(void) { - size_t data_length = 0; - size_t read_chars = 0; + size_t data_length = 0; + size_t read_chars = 0; - char *xml_buffer = NULL; - xmlNode *xml_obj = NULL; + char *xml_buffer = NULL; + xmlNode *xml_obj = NULL; - do { - crm_realloc(xml_buffer, XML_BUFFER_SIZE + data_length + 1); - read_chars = fread(xml_buffer + data_length, 1, XML_BUFFER_SIZE, stdin); - data_length += read_chars; - } while (read_chars > 0); - - if(data_length == 0) { - crm_warn("No XML supplied on stdin"); - crm_free(xml_buffer); - return NULL; - } - - xml_buffer[data_length] = '\0'; + do { + crm_realloc(xml_buffer, XML_BUFFER_SIZE + data_length + 1); + read_chars = fread(xml_buffer + data_length, 1, XML_BUFFER_SIZE, stdin); + data_length += read_chars; + } while (read_chars > 0); - xml_obj = string2xml(xml_buffer); + if(data_length == 0) { + crm_warn("No XML supplied on stdin"); crm_free(xml_buffer); + return NULL; + } - crm_log_xml_debug_3(xml_obj, "Created fragment"); - return xml_obj; + xml_buffer[data_length] = '\0'; + + xml_obj = string2xml(xml_buffer); + crm_free(xml_buffer); + + crm_log_xml_debug_3(xml_obj, "Created fragment"); + return xml_obj; } static char * decompress_file(const char *filename) { char *buffer = NULL; #if HAVE_BZLIB_H int rc = 0; size_t length = 0, read_len = 0; BZFILE *bz_file = NULL; FILE *input = fopen(filename, "r"); if(input == NULL) { crm_perror(LOG_ERR,"Could not open %s for reading", filename); return NULL; } bz_file = BZ2_bzReadOpen(&rc, input, 0, 0, NULL, 0); if ( rc != BZ_OK ) { BZ2_bzReadClose ( &rc, bz_file); return NULL; } rc = BZ_OK; while ( rc == BZ_OK ) { crm_realloc(buffer, XML_BUFFER_SIZE + length + 1); read_len = BZ2_bzRead ( &rc, bz_file, buffer + length, XML_BUFFER_SIZE); crm_debug_5("Read %ld bytes from file: %d", (long)read_len, rc); if ( rc == BZ_OK || rc == BZ_STREAM_END) { length += read_len; } } buffer[length] = '\0'; read_len = length; if ( rc != BZ_STREAM_END ) { crm_err("Couldnt read compressed xml from file"); crm_free(buffer); buffer = NULL; } BZ2_bzReadClose (&rc, bz_file); fclose(input); #else crm_err("Cannot read compressed files:" " bzlib was not available at compile time"); #endif return buffer; } xmlNode * filename2xml(const char *filename) { xmlNode *xml = NULL; xmlDocPtr output = NULL; xmlParserCtxtPtr ctxt = NULL; xmlErrorPtr last_error = NULL; static int xml_options = XML_PARSE_NOBLANKS|XML_PARSE_RECOVER; /* create a parser context */ ctxt = xmlNewParserCtxt(); CRM_CHECK(ctxt != NULL, return NULL); /* xmlCtxtUseOptions(ctxt, XML_PARSE_NOBLANKS|XML_PARSE_RECOVER); */ xmlCtxtResetLastError(ctxt); xmlSetGenericErrorFunc(ctxt, crm_xml_err); /* initGenericErrorDefaultFunc(crm_xml_err); */ if(filename == NULL) { /* STDIN_FILENO == fileno(stdin) */ output = xmlCtxtReadFd(ctxt, STDIN_FILENO, "unknown.xml", NULL, xml_options); } else if(strstr(filename, ".bz2") == NULL) { output = xmlCtxtReadFile(ctxt, filename, NULL, xml_options); } else { char *input = decompress_file(filename); output = xmlCtxtReadDoc(ctxt, (const xmlChar*)input, NULL, NULL, xml_options); crm_free(input); } if(output) { xml = xmlDocGetRootElement(output); } last_error = xmlCtxtGetLastError(ctxt); if(last_error && last_error->code != XML_ERR_OK) { /* crm_abort(__FILE__,__PRETTY_FUNCTION__,__LINE__, "last_error->code != XML_ERR_OK", TRUE, TRUE); */ /* * http://xmlsoft.org/html/libxml-xmlerror.html#xmlErrorLevel * http://xmlsoft.org/html/libxml-xmlerror.html#xmlParserErrors */ crm_err("Parsing failed (domain=%d, level=%d, code=%d): %s", last_error->domain, last_error->level, last_error->code, last_error->message); if(last_error && last_error->code != XML_ERR_OK) { crm_err("Couldn't%s parse %s", xml?" fully":"", filename); if(xml != NULL) { crm_log_xml_err(xml, "Partial"); } } } xmlFreeParserCtxt(ctxt); return xml; } int write_xml_file(xmlNode *xml_node, const char *filename, gboolean compress) { - int res = 0; - time_t now; - char *buffer = NULL; - char *now_str = NULL; - unsigned int out = 0; - FILE *file_output_strm = NULL; - static mode_t cib_mode = S_IRUSR|S_IWUSR; - - CRM_CHECK(filename != NULL, return -1); - - crm_debug_3("Writing XML out to %s", filename); - crm_validate_data(xml_node); - if (xml_node == NULL) { - crm_err("Cannot write NULL to %s", filename); - return -1; - } + int res = 0; + time_t now; + char *buffer = NULL; + char *now_str = NULL; + unsigned int out = 0; + FILE *file_output_strm = NULL; + static mode_t cib_mode = S_IRUSR|S_IWUSR; + + CRM_CHECK(filename != NULL, return -1); - file_output_strm = fopen(filename, "w"); - if(file_output_strm == NULL) { - crm_perror(LOG_ERR,"Cannot open %s for writing", filename); - return -1; - } + crm_debug_3("Writing XML out to %s", filename); + crm_validate_data(xml_node); + if (xml_node == NULL) { + crm_err("Cannot write NULL to %s", filename); + return -1; + } + + file_output_strm = fopen(filename, "w"); + if(file_output_strm == NULL) { + crm_perror(LOG_ERR,"Cannot open %s for writing", filename); + return -1; + } - /* establish the correct permissions */ - fchmod(fileno(file_output_strm), cib_mode); + /* establish the correct permissions */ + fchmod(fileno(file_output_strm), cib_mode); - crm_log_xml_debug_4(xml_node, "Writing out"); + crm_log_xml_debug_4(xml_node, "Writing out"); - now = time(NULL); - now_str = ctime(&now); - now_str[24] = EOS; /* replace the newline */ - crm_xml_add(xml_node, XML_CIB_ATTR_WRITTEN, now_str); - crm_validate_data(xml_node); + now = time(NULL); + now_str = ctime(&now); + now_str[24] = EOS; /* replace the newline */ + crm_xml_add(xml_node, XML_CIB_ATTR_WRITTEN, now_str); + crm_validate_data(xml_node); - buffer = dump_xml_formatted(xml_node); - CRM_CHECK(buffer != NULL && strlen(buffer) > 0, - crm_log_xml_warn(xml_node, "dump:failed"); - goto bail); + buffer = dump_xml_formatted(xml_node); + CRM_CHECK(buffer != NULL && strlen(buffer) > 0, + crm_log_xml_warn(xml_node, "dump:failed"); + goto bail); - if(compress) { + if(compress) { #if HAVE_BZLIB_H - int rc = BZ_OK; - unsigned int in = 0; - BZFILE *bz_file = NULL; - bz_file = BZ2_bzWriteOpen(&rc, file_output_strm, 5, 0, 30); + int rc = BZ_OK; + unsigned int in = 0; + BZFILE *bz_file = NULL; + bz_file = BZ2_bzWriteOpen(&rc, file_output_strm, 5, 0, 30); + if(rc != BZ_OK) { + crm_err("bzWriteOpen failed: %d", rc); + } else { + BZ2_bzWrite(&rc,bz_file,buffer,strlen(buffer)); if(rc != BZ_OK) { - crm_err("bzWriteOpen failed: %d", rc); - } else { - BZ2_bzWrite(&rc,bz_file,buffer,strlen(buffer)); - if(rc != BZ_OK) { - crm_err("bzWrite() failed: %d", rc); - } + crm_err("bzWrite() failed: %d", rc); } + } - if(rc == BZ_OK) { - BZ2_bzWriteClose(&rc, bz_file, 0, &in, &out); - if(rc != BZ_OK) { - crm_err("bzWriteClose() failed: %d",rc); - out = -1; - } else { - crm_debug_2("%s: In: %d, out: %d", filename, in, out); - } + if(rc == BZ_OK) { + BZ2_bzWriteClose(&rc, bz_file, 0, &in, &out); + if(rc != BZ_OK) { + crm_err("bzWriteClose() failed: %d",rc); + out = -1; + } else { + crm_debug_2("%s: In: %d, out: %d", filename, in, out); } + } #else - crm_err("Cannot write compressed files:" - " bzlib was not available at compile time"); + crm_err("Cannot write compressed files:" + " bzlib was not available at compile time"); #endif - } + } - if(out <= 0) { - res = fprintf(file_output_strm, "%s", buffer); - if(res < 0) { - crm_perror(LOG_ERR,"Cannot write output to %s", filename); - goto bail; - } - } + if(out <= 0) { + res = fprintf(file_output_strm, "%s", buffer); + if(res < 0) { + crm_perror(LOG_ERR,"Cannot write output to %s", filename); + goto bail; + } + } bail: - if(fflush(file_output_strm) != 0) { - crm_perror(LOG_ERR,"fflush for %s failed:", filename); - res = -1; - } + if(fflush(file_output_strm) != 0) { + crm_perror(LOG_ERR,"fflush for %s failed:", filename); + res = -1; + } - if(fsync(fileno(file_output_strm)) < 0) { - crm_perror(LOG_ERR,"fsync for %s failed:", filename); - res = -1; - } + if(fsync(fileno(file_output_strm)) < 0) { + crm_perror(LOG_ERR,"fsync for %s failed:", filename); + res = -1; + } - fclose(file_output_strm); + fclose(file_output_strm); - crm_debug_3("Saved %d bytes to the Cib as XML", res); - crm_free(buffer); + crm_debug_3("Saved %d bytes to the Cib as XML", res); + crm_free(buffer); - return res; + return res; } static HA_Message* convert_xml_message_struct(HA_Message *parent, xmlNode *src_node, const char *field) { xmlNode *child = NULL; xmlNode *__crm_xml_iter = src_node->children; xmlAttrPtr prop_iter = src_node->properties; const char *name = NULL; const char *value = NULL; HA_Message *result = ha_msg_new(3); ha_msg_add(result, F_XML_TAGNAME, (const char *)src_node->name); while(prop_iter != NULL) { name = (const char *)prop_iter->name; value = (const char *)xmlGetProp(src_node, prop_iter->name); prop_iter = prop_iter->next; ha_msg_add(result, name, value); } while(__crm_xml_iter != NULL) { child = __crm_xml_iter; __crm_xml_iter = __crm_xml_iter->next; convert_xml_message_struct(result, child, NULL); } if(parent == NULL) { return result; } if(field) { HA_Message *holder = ha_msg_new(3); CRM_ASSERT(holder != NULL); ha_msg_add(holder, F_XML_TAGNAME, field); add_ha_nocopy(holder, result, (const char*)src_node->name); ha_msg_addstruct_compress(parent, field, holder); ha_msg_del(holder); } else { add_ha_nocopy(parent, result, (const char*)src_node->name); } return result; } static void convert_xml_child(HA_Message *msg, xmlNode *xml) { int orig = 0; int rc = BZ_OK; unsigned int len = 0; char *buffer = NULL; char *compressed = NULL; const char *name = NULL; name = (const char *)xml->name; buffer = dump_xml_unformatted(xml); orig = strlen(buffer); if(orig < CRM_BZ2_THRESHOLD) { ha_msg_add(msg, name, buffer); goto done; } len = (orig * 1.1) + 600; /* recomended size */ crm_malloc(compressed, len); rc = BZ2_bzBuffToBuffCompress(compressed, &len, buffer, orig, CRM_BZ2_BLOCKS, 0, CRM_BZ2_WORK); if(rc != BZ_OK) { crm_err("Compression failed: %d", rc); crm_free(compressed); convert_xml_message_struct(msg, xml, name); goto done; } crm_free(buffer); buffer = compressed; crm_debug_2("Compression details: %d -> %d", orig, len); ha_msg_addbin(msg, name, buffer, len); done: crm_free(buffer); # if 0 { unsigned int used = orig; char *uncompressed = NULL; crm_debug("Trying to decompress %d bytes", len); crm_malloc0(uncompressed, orig); rc = BZ2_bzBuffToBuffDecompress( uncompressed, &used, compressed, len, 1, 0); CRM_CHECK(rc == BZ_OK, ;); CRM_CHECK(used == orig, ;); crm_debug("rc=%d, used=%d", rc, used); if(rc != BZ_OK) { exit(100); } crm_debug("Original %s, decompressed %s", buffer, uncompressed); crm_free(uncompressed); } # endif } HA_Message* convert_xml_message(xmlNode *xml) { + xmlNode *child = NULL; HA_Message *result = NULL; result = ha_msg_new(3); ha_msg_add(result, F_XML_TAGNAME, (const char *)xml->name); xml_prop_iter(xml, name, value, ha_msg_add(result, name, value)); - xml_child_iter(xml, child, convert_xml_child(result, child)); + for(child = xml; child != NULL; child = child->next) { + convert_xml_child(result, child); + } return result; } static void convert_ha_field(xmlNode *parent, HA_Message *msg, int lpc) { int type = 0; const char *name = NULL; const char *value = NULL; xmlNode *xml = NULL; int rc = BZ_OK; size_t orig_len = 0; unsigned int used = 0; char *uncompressed = NULL; char *compressed = NULL; int size = orig_len * 10; CRM_CHECK(parent != NULL, return); CRM_CHECK(msg != NULL, return); name = msg->names[lpc]; type = cl_get_type(msg, name); switch(type) { case FT_STRUCT: convert_ha_message(parent, msg->values[lpc], name); break; case FT_COMPRESS: case FT_UNCOMPRESS: convert_ha_message(parent, cl_get_struct(msg, name), name); break; case FT_STRING: value = msg->values[lpc]; CRM_CHECK(value != NULL, return); crm_debug_5("Converting %s/%d/%s", name, type, value[0] == '<' ? "xml":"field"); if( value[0] != '<' ) { crm_xml_add(parent, name, value); break; } /* unpack xml string */ xml = string2xml(value); if(xml == NULL) { crm_err("Conversion of field '%s' failed", name); return; } add_node_nocopy(parent, NULL, xml); break; case FT_BINARY: value = cl_get_binary(msg, name, &orig_len); size = orig_len * 10 + 1; /* +1 because an exact 10x compression factor happens occasionally */ if(orig_len < 3 || value[0] != 'B' || value[1] != 'Z' || value[2] != 'h') { if(strstr(name, "uuid") == NULL) { crm_err("Skipping non-bzip binary field: %s", name); } return; } crm_malloc0(compressed, orig_len); memcpy(compressed, value, orig_len); crm_debug_2("Trying to decompress %d bytes", (int)orig_len); - retry: + retry: crm_realloc(uncompressed, size); memset(uncompressed, 0, size); used = size - 1; /* always leave room for a trailing '\0' * BZ2_bzBuffToBuffDecompress wont say anything if * the uncompressed data is exactly 'size' bytes */ rc = BZ2_bzBuffToBuffDecompress( uncompressed, &used, compressed, orig_len, 1, 0); if(rc == BZ_OUTBUFF_FULL) { size = size * 2; /* dont try to allocate more memory than we have */ if(size > 0) { goto retry; } } if(rc != BZ_OK) { crm_err("Decompression of %s (%d bytes) into %d failed: %d", name, (int)orig_len, size, rc); } else { CRM_ASSERT(used < size); CRM_CHECK(uncompressed[used] == 0, uncompressed[used] = 0); xml = string2xml(uncompressed); } if(xml != NULL) { add_node_copy(parent, xml); free_xml(xml); } crm_free(uncompressed); crm_free(compressed); break; } } xmlNode * convert_ha_message(xmlNode *parent, HA_Message *msg, const char *field) { int lpc = 0; xmlNode *child = NULL; const char *tag = NULL; CRM_CHECK(msg != NULL, crm_err("Empty message for %s", field); return parent); tag = cl_get_string(msg, F_XML_TAGNAME); if(tag == NULL) { tag = field; } else if(parent && safe_str_neq(field, tag)) { /* For compatability with 0.6.x */ crm_debug("Creating intermediate parent %s between %s and %s", field, crm_element_name(parent), tag); parent = create_xml_node(parent, field); } if(parent == NULL) { parent = create_xml_node(NULL, tag); child = parent; } else { child = create_xml_node(parent, tag); } for (lpc = 0; lpc < msg->nfields; lpc++) { convert_ha_field(child, msg, lpc); } return parent; } xmlNode *convert_ipc_message(IPC_Message *msg, const char *field) { HA_Message *hmsg = wirefmt2msg((char *)msg->msg_body, msg->msg_len, 0); xmlNode *xml = convert_ha_message(NULL, hmsg, __FUNCTION__); crm_msg_del(hmsg); return xml; } xmlNode * get_message_xml(xmlNode *msg, const char *field) { xmlNode *tmp = first_named_child(msg, field); return first_named_child(tmp, NULL); } gboolean add_message_xml(xmlNode *msg, const char *field, xmlNode *xml) { xmlNode *holder = create_xml_node(msg, field); add_node_copy(holder, xml); return TRUE; } static char * dump_xml(xmlNode *an_xml_node, gboolean formatted, gboolean for_digest) { int len = 0; char *buffer = NULL; xmlBuffer *xml_buffer = NULL; xmlDoc *doc = getDocPtr(an_xml_node); /* doc will only be NULL if an_xml_node is */ CRM_CHECK(doc != NULL, return NULL); xml_buffer = xmlBufferCreate(); CRM_ASSERT(xml_buffer != NULL); len = xmlNodeDump(xml_buffer, doc, an_xml_node, 0, formatted); if(len > 0) { /* The copying here isn't ideal, but it doesn't even register * in the perf numbers */ if(for_digest) { /* for compatability with the old result which is used for digests */ len += 3; crm_malloc0(buffer, len); snprintf(buffer, len, " %s\n", (char *)xml_buffer->content); } else { buffer = crm_strdup((char *)xml_buffer->content); } } else { crm_err("Conversion failed"); } xmlBufferFree(xml_buffer); return buffer; } char * dump_xml_formatted(xmlNode *an_xml_node) { return dump_xml(an_xml_node, TRUE, FALSE); } char * dump_xml_unformatted(xmlNode *an_xml_node) { return dump_xml(an_xml_node, FALSE, FALSE); } -#define update_buffer() do { \ - if(printed < 0) { \ - crm_perror(LOG_ERR,"snprintf failed"); \ - goto print; \ - } else if(printed >= (buffer_len - offset)) { \ +#define update_buffer() do { \ + if(printed < 0) { \ + crm_perror(LOG_ERR,"snprintf failed"); \ + goto print; \ + } else if(printed >= (buffer_len - offset)) { \ crm_err("Output truncated: available=%d, needed=%d", buffer_len - offset, printed); \ - offset += printed; \ - goto print; \ - } else if(offset >= buffer_len) { \ - crm_err("Buffer exceeded"); \ - offset += printed; \ - goto print; \ - } else { \ - offset += printed; \ - } \ -} while(0) + offset += printed; \ + goto print; \ + } else if(offset >= buffer_len) { \ + crm_err("Buffer exceeded"); \ + offset += printed; \ + goto print; \ + } else { \ + offset += printed; \ + } \ + } while(0) int print_spaces(char *buffer, int depth, int max) { - int lpc = 0; - int spaces = 2*depth; - max--; + int lpc = 0; + int spaces = 2*depth; + max--; - /* <= so that we always print 1 space - prevents problems with syslog */ - for(lpc = 0; lpc <= spaces && lpc < max; lpc++) { - if(sprintf(buffer+lpc, "%c", ' ') < 1) { - return -1; - } + /* <= so that we always print 1 space - prevents problems with syslog */ + for(lpc = 0; lpc <= spaces && lpc < max; lpc++) { + if(sprintf(buffer+lpc, "%c", ' ') < 1) { + return -1; } - return lpc; + } + return lpc; } int log_data_element( int log_level, const char *file, const char *function, int line, const char *prefix, xmlNode *data, int depth, gboolean formatted) { - int child_result = 0; + xmlNode *a_child = NULL; + int child_result = 0; - int offset = 0; - int printed = 0; - char *buffer = NULL; - int buffer_len = 1000; + int offset = 0; + int printed = 0; + char *buffer = NULL; + int buffer_len = 1000; - const char *name = NULL; - const char *hidden = NULL; + const char *name = NULL; + const char *hidden = NULL; - if(data == NULL) { - crm_warn("No data to dump as XML"); - return 0; - } + if(data == NULL) { + crm_warn("No data to dump as XML"); + return 0; + } - name = crm_element_name(data); - CRM_ASSERT(name != NULL); + name = crm_element_name(data); + CRM_ASSERT(name != NULL); - crm_debug_5("Dumping %s", name); - crm_malloc0(buffer, buffer_len); + crm_debug_5("Dumping %s", name); + crm_malloc0(buffer, buffer_len); - if(formatted) { - offset = print_spaces(buffer, depth, buffer_len - offset); - } + if(formatted) { + offset = print_spaces(buffer, depth, buffer_len - offset); + } - printed = snprintf(buffer + offset, buffer_len - offset, "<%s", name); - update_buffer(); + printed = snprintf(buffer + offset, buffer_len - offset, "<%s", name); + update_buffer(); - hidden = crm_element_value(data, "hidden"); - xml_prop_iter( - data, prop_name, prop_value, + hidden = crm_element_value(data, "hidden"); + xml_prop_iter( + data, prop_name, prop_value, - if(prop_name == NULL - || safe_str_eq(F_XML_TAGNAME, prop_name)) { - continue; + if(prop_name == NULL + || safe_str_eq(F_XML_TAGNAME, prop_name)) { + continue; - } else if(hidden != NULL - && prop_name[0] != 0 - && strstr(hidden, prop_name) != NULL) { - prop_value = "*****"; - } + } else if(hidden != NULL + && prop_name[0] != 0 + && strstr(hidden, prop_name) != NULL) { + prop_value = "*****"; + } - crm_debug_5("Dumping <%s %s=\"%s\"...", - name, prop_name, prop_value); - printed = snprintf(buffer + offset, buffer_len - offset, - " %s=\"%s\"", prop_name, prop_value); - update_buffer(); - ); - + crm_debug_5("Dumping <%s %s=\"%s\"...", + name, prop_name, prop_value); printed = snprintf(buffer + offset, buffer_len - offset, - " %s>", xml_has_children(data)?"":"/"); + " %s=\"%s\"", prop_name, prop_value); update_buffer(); + ); + + printed = snprintf(buffer + offset, buffer_len - offset, + " %s>", xml_has_children(data)?"":"/"); + update_buffer(); print: - do_crm_log_alias(log_level, file, function, line, "%s%s", prefix?prefix:"", buffer); + do_crm_log_alias(log_level, file, function, line, "%s%s", prefix?prefix:"", buffer); - if(xml_has_children(data) == FALSE) { - crm_free(buffer); - return 0; - } + if(xml_has_children(data) == FALSE) { + crm_free(buffer); + return 0; + } - xml_child_iter( - data, a_child, - child_result = log_data_element( - log_level, file, function, line, prefix, a_child, depth+1, formatted); - ); + for(a_child = data; a_child != NULL; a_child = a_child->next) { + child_result = log_data_element( + log_level, file, function, line, prefix, a_child, depth+1, formatted); + } - if(formatted) { - offset = print_spaces(buffer, depth, buffer_len); - } - do_crm_log_alias(log_level, file, function, line, "%s%s", prefix?prefix:"", buffer, name); - crm_free(buffer); - return 1; + if(formatted) { + offset = print_spaces(buffer, depth, buffer_len); + } + do_crm_log_alias(log_level, file, function, line, "%s%s", prefix?prefix:"", buffer, name); + crm_free(buffer); + return 1; } gboolean xml_has_children(const xmlNode *xml_root) { - if(xml_root != NULL && xml_root->children != NULL) { - return TRUE; - } - return FALSE; + if(xml_root != NULL && xml_root->children != NULL) { + return TRUE; + } + return FALSE; } void xml_validate(const xmlNode *xml_root) { - CRM_ASSERT(xml_root != NULL); + CRM_ASSERT(xml_root != NULL); } int crm_element_value_int(xmlNode *data, const char *name, int *dest) { const char *value = crm_element_value(data, name); CRM_CHECK(dest != NULL, return -1); if(value) { *dest = crm_int_helper(value, NULL); return 0; } return -1; } const char * crm_element_value_const(const xmlNode *data, const char *name) { return crm_element_value((xmlNode*)data, name); } char * crm_element_value_copy(xmlNode *data, const char *name) { - char *value_copy = NULL; - const char *value = crm_element_value(data, name); - if(value != NULL) { - value_copy = crm_strdup(value); - } - return value_copy; + char *value_copy = NULL; + const char *value = crm_element_value(data, name); + if(value != NULL) { + value_copy = crm_strdup(value); + } + return value_copy; } void xml_remove_prop(xmlNode *obj, const char *name) { xmlUnsetProp(obj, (const xmlChar*)name); } void log_xml_diff(unsigned int log_level, xmlNode *diff, const char *function) { - xmlNode *added = find_xml_node(diff, "diff-added", FALSE); - xmlNode *removed = find_xml_node(diff, "diff-removed", FALSE); - gboolean is_first = TRUE; + xmlNode *child = NULL; + xmlNode *added = find_xml_node(diff, "diff-added", FALSE); + xmlNode *removed = find_xml_node(diff, "diff-removed", FALSE); + gboolean is_first = TRUE; - if(crm_log_level < log_level) { - /* nothing will ever be printed */ - return; - } + if(crm_log_level < log_level) { + /* nothing will ever be printed */ + return; + } - xml_child_iter( - removed, child, - log_data_element(log_level, NULL, function, 0, "-", child, 0, TRUE); - if(is_first) { - is_first = FALSE; - } else { - do_crm_log(log_level, " --- "); - } - - ); - is_first = TRUE; - xml_child_iter( - added, child, - log_data_element(log_level, NULL, function, 0, "+", child, 0, TRUE); - if(is_first) { - is_first = FALSE; - } else { - do_crm_log(log_level, " +++ "); - } - ); + for(child = removed; child != NULL; child = child->next) { + log_data_element(log_level, NULL, function, 0, "-", child, 0, TRUE); + if(is_first) { + is_first = FALSE; + } else { + do_crm_log(log_level, " --- "); + } + } + + is_first = TRUE; + for(child = added; child != NULL; child = child->next) { + log_data_element(log_level, NULL, function, 0, "+", child, 0, TRUE); + if(is_first) { + is_first = FALSE; + } else { + do_crm_log(log_level, " +++ "); + } + } } void purge_diff_markers(xmlNode *a_node) { - CRM_CHECK(a_node != NULL, return); + xmlNode *child = NULL; + CRM_CHECK(a_node != NULL, return); - xml_remove_prop(a_node, XML_DIFF_MARKER); - xml_child_iter(a_node, child, - purge_diff_markers(child); - ); + xml_remove_prop(a_node, XML_DIFF_MARKER); + for(child = a_node; child != NULL; child = child->next) { + purge_diff_markers(child); + } } gboolean apply_xml_diff(xmlNode *old, xmlNode *diff, xmlNode **new) { - gboolean result = TRUE; - int root_nodes_seen = 0; - const char *digest = crm_element_value(diff, XML_ATTR_DIGEST); - const char *version = crm_element_value(diff, XML_ATTR_CRM_VERSION); - xmlNode *added = find_xml_node(diff, "diff-added", FALSE); - xmlNode *removed = find_xml_node(diff, "diff-removed", FALSE); - - CRM_CHECK(new != NULL, return FALSE); - - crm_debug_2("Substraction Phase"); - xml_child_iter(removed, child_diff, - CRM_CHECK(root_nodes_seen == 0, result = FALSE); - if(root_nodes_seen == 0) { - *new = subtract_xml_object(NULL, old, child_diff, FALSE, NULL); - } - root_nodes_seen++; - ); + gboolean result = TRUE; + int root_nodes_seen = 0; + const char *digest = crm_element_value(diff, XML_ATTR_DIGEST); + const char *version = crm_element_value(diff, XML_ATTR_CRM_VERSION); + + xmlNode *child_diff = NULL; + xmlNode *added = find_xml_node(diff, "diff-added", FALSE); + xmlNode *removed = find_xml_node(diff, "diff-removed", FALSE); + + CRM_CHECK(new != NULL, return FALSE); + + crm_debug_2("Substraction Phase"); + for(child_diff = removed; child_diff != NULL; child_diff = child_diff->next) { + CRM_CHECK(root_nodes_seen == 0, result = FALSE); if(root_nodes_seen == 0) { - *new = copy_xml(old); - - } else if(root_nodes_seen > 1) { - crm_err("(-) Diffs cannot contain more than one change set..." - " saw %d", root_nodes_seen); - result = FALSE; + *new = subtract_xml_object(NULL, old, child_diff, FALSE, NULL); } + root_nodes_seen++; + } + + if(root_nodes_seen == 0) { + *new = copy_xml(old); + + } else if(root_nodes_seen > 1) { + crm_err("(-) Diffs cannot contain more than one change set..." + " saw %d", root_nodes_seen); + result = FALSE; + } - root_nodes_seen = 0; - crm_debug_2("Addition Phase"); - if(result) { - xml_child_iter(added, child_diff, - CRM_CHECK(root_nodes_seen == 0, result = FALSE); - if(root_nodes_seen == 0) { - add_xml_object(NULL, *new, child_diff, TRUE); - } - root_nodes_seen++; - ); + root_nodes_seen = 0; + crm_debug_2("Addition Phase"); + if(result) { + xmlNode *child_diff = NULL; + for(child_diff = added; child_diff != NULL; child_diff = child_diff->next) { + CRM_CHECK(root_nodes_seen == 0, result = FALSE); + if(root_nodes_seen == 0) { + add_xml_object(NULL, *new, child_diff, TRUE); + } + root_nodes_seen++; } + } - if(root_nodes_seen > 1) { - crm_err("(+) Diffs cannot contain more than one change set..." - " saw %d", root_nodes_seen); - result = FALSE; - - } else if(result && digest) { - char *new_digest = NULL; - purge_diff_markers(*new); /* Purge now so the diff is ok */ - new_digest = calculate_xml_versioned_digest(*new, FALSE, TRUE, version); - if(safe_str_neq(new_digest, digest)) { - crm_info("Digest mis-match: expected %s, calculated %s", - digest, new_digest); - crm_log_xml_trace(old, "diff:original"); - crm_log_xml_trace(diff, "diff:input"); - result = FALSE; - } else { - crm_debug_2("Digest matched: expected %s, calculated %s", - digest, new_digest); - } - crm_free(new_digest); + if(root_nodes_seen > 1) { + crm_err("(+) Diffs cannot contain more than one change set..." + " saw %d", root_nodes_seen); + result = FALSE; + + } else if(result && digest) { + char *new_digest = NULL; + purge_diff_markers(*new); /* Purge now so the diff is ok */ + new_digest = calculate_xml_versioned_digest(*new, FALSE, TRUE, version); + if(safe_str_neq(new_digest, digest)) { + crm_info("Digest mis-match: expected %s, calculated %s", + digest, new_digest); + crm_log_xml_trace(old, "diff:original"); + crm_log_xml_trace(diff, "diff:input"); + result = FALSE; + } else { + crm_debug_2("Digest matched: expected %s, calculated %s", + digest, new_digest); + } + crm_free(new_digest); #if XML_PARANOIA_CHECKS - } else if(result) { - int lpc = 0; - xmlNode *intermediate = NULL; - xmlNode *diff_of_diff = NULL; - xmlNode *calc_added = NULL; - xmlNode *calc_removed = NULL; - - const char *value = NULL; - const char *name = NULL; - const char *version_attrs[] = { - XML_ATTR_NUMUPDATES, - XML_ATTR_GENERATION, - XML_ATTR_GENERATION_ADMIN - }; - - crm_debug_2("Verification Phase"); - intermediate = diff_xml_object(old, *new, FALSE); - calc_added = find_xml_node(intermediate, "diff-added", FALSE); - calc_removed = find_xml_node(intermediate, "diff-removed", FALSE); - - /* add any version details to the diff so they match */ - for(lpc = 0; lpc < DIMOF(version_attrs); lpc++) { - name = version_attrs[lpc]; - - value = crm_element_value(added, name); - crm_xml_add(calc_added, name, value); + } else if(result) { + int lpc = 0; + xmlNode *intermediate = NULL; + xmlNode *diff_of_diff = NULL; + xmlNode *calc_added = NULL; + xmlNode *calc_removed = NULL; + + const char *value = NULL; + const char *name = NULL; + const char *version_attrs[] = { + XML_ATTR_NUMUPDATES, + XML_ATTR_GENERATION, + XML_ATTR_GENERATION_ADMIN + }; + + crm_debug_2("Verification Phase"); + intermediate = diff_xml_object(old, *new, FALSE); + calc_added = find_xml_node(intermediate, "diff-added", FALSE); + calc_removed = find_xml_node(intermediate, "diff-removed", FALSE); + + /* add any version details to the diff so they match */ + for(lpc = 0; lpc < DIMOF(version_attrs); lpc++) { + name = version_attrs[lpc]; + + value = crm_element_value(added, name); + crm_xml_add(calc_added, name, value); - value = crm_element_value(removed, name); - crm_xml_add(calc_removed, name, value); - } + value = crm_element_value(removed, name); + crm_xml_add(calc_removed, name, value); + } - diff_of_diff = diff_xml_object(intermediate, diff, TRUE); - if(diff_of_diff != NULL) { - crm_info("Diff application failed!"); - crm_log_xml_debug(old, "diff:original"); - crm_log_xml_debug(diff, "diff:input"); - result = FALSE; - } else { - purge_diff_markers(*new); - } + diff_of_diff = diff_xml_object(intermediate, diff, TRUE); + if(diff_of_diff != NULL) { + crm_info("Diff application failed!"); + crm_log_xml_debug(old, "diff:original"); + crm_log_xml_debug(diff, "diff:input"); + result = FALSE; + } else { + purge_diff_markers(*new); + } - free_xml(diff_of_diff); - free_xml(intermediate); - diff_of_diff = NULL; - intermediate = NULL; + free_xml(diff_of_diff); + free_xml(intermediate); + diff_of_diff = NULL; + intermediate = NULL; #endif - } + } - return result; + return result; } xmlNode * diff_xml_object(xmlNode *old, xmlNode *new, gboolean suppress) { - xmlNode *tmp1 = NULL; - xmlNode *diff = create_xml_node(NULL, "diff"); - xmlNode *removed = create_xml_node(diff, "diff-removed"); - xmlNode *added = create_xml_node(diff, "diff-added"); + xmlNode *tmp1 = NULL; + xmlNode *diff = create_xml_node(NULL, "diff"); + xmlNode *removed = create_xml_node(diff, "diff-removed"); + xmlNode *added = create_xml_node(diff, "diff-added"); - crm_xml_add(diff, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET); + crm_xml_add(diff, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET); - tmp1 = subtract_xml_object(removed, old, new, FALSE, "removed:top"); - if(suppress && tmp1 != NULL && can_prune_leaf(tmp1)) { - free_xml_from_parent(removed, tmp1); - } + tmp1 = subtract_xml_object(removed, old, new, FALSE, "removed:top"); + if(suppress && tmp1 != NULL && can_prune_leaf(tmp1)) { + free_xml_from_parent(removed, tmp1); + } - tmp1 = subtract_xml_object(added, new, old, TRUE, "added:top"); - if(suppress && tmp1 != NULL && can_prune_leaf(tmp1)) { - free_xml_from_parent(added, tmp1); - } + tmp1 = subtract_xml_object(added, new, old, TRUE, "added:top"); + if(suppress && tmp1 != NULL && can_prune_leaf(tmp1)) { + free_xml_from_parent(added, tmp1); + } - if(added->children == NULL && removed->children == NULL) { - free_xml(diff); - diff = NULL; - } + if(added->children == NULL && removed->children == NULL) { + free_xml(diff); + diff = NULL; + } - return diff; + return diff; } gboolean can_prune_leaf(xmlNode *xml_node) { - gboolean can_prune = TRUE; + xmlNode *child = NULL; + gboolean can_prune = TRUE; /* return FALSE; */ - xml_prop_name_iter(xml_node, prop_name, - if(safe_str_eq(prop_name, XML_ATTR_ID)) { - continue; - } - can_prune = FALSE; - ); - xml_child_iter(xml_node, child, - if(can_prune_leaf(child)) { - free_xml(child); - } else { - can_prune = FALSE; - } - ); - return can_prune; + xml_prop_name_iter(xml_node, prop_name, + if(safe_str_eq(prop_name, XML_ATTR_ID)) { + continue; + } + can_prune = FALSE; + ); + for(child = xml_node; child != NULL; child = child->next) { + if(can_prune_leaf(child)) { + free_xml(child); + } else { + can_prune = FALSE; + } + } + return can_prune; } void diff_filter_context(int context, int upper_bound, int lower_bound, xmlNode *xml_node, xmlNode *parent) { - xmlNode *us = NULL; - xmlNode *new_parent = parent; - const char *name = crm_element_name(xml_node); + xmlNode *us = NULL; + xmlNode *child = NULL; + xmlNode *new_parent = parent; + const char *name = crm_element_name(xml_node); - CRM_CHECK(xml_node != NULL && name != NULL, return); + CRM_CHECK(xml_node != NULL && name != NULL, return); - us = create_xml_node(parent, name); - xml_prop_iter(xml_node, prop_name, prop_value, - lower_bound = context; - crm_xml_add(us, prop_name, prop_value); - ); - if(lower_bound >= 0 || upper_bound >= 0) { - crm_xml_add(us, XML_ATTR_ID, ID(xml_node)); - new_parent = us; + us = create_xml_node(parent, name); + xml_prop_iter(xml_node, prop_name, prop_value, + lower_bound = context; + crm_xml_add(us, prop_name, prop_value); + ); + if(lower_bound >= 0 || upper_bound >= 0) { + crm_xml_add(us, XML_ATTR_ID, ID(xml_node)); + new_parent = us; + } else { + upper_bound = in_upper_context(0, context, xml_node); + if(upper_bound >= 0) { + crm_xml_add(us, XML_ATTR_ID, ID(xml_node)); + new_parent = us; } else { - upper_bound = in_upper_context(0, context, xml_node); - if(upper_bound >= 0) { - crm_xml_add(us, XML_ATTR_ID, ID(xml_node)); - new_parent = us; - } else { - free_xml(us); - us = NULL; - } + free_xml(us); + us = NULL; } + } - xml_child_iter(us, child, - diff_filter_context( - context, upper_bound-1, lower_bound-1, - child, new_parent); - ); + for(child = us; child != NULL; child = child->next) { + diff_filter_context(context, upper_bound-1, lower_bound-1, + child, new_parent); + } } int in_upper_context(int depth, int context, xmlNode *xml_node) { - gboolean has_attributes = FALSE; - if(context == 0) { - return 0; - } + gboolean has_attributes = FALSE; + if(context == 0) { + return 0; + } - xml_prop_name_iter(xml_node, prop_name, - has_attributes = TRUE; - break; - ); + xml_prop_name_iter(xml_node, prop_name, has_attributes = TRUE; break); - if(has_attributes) { - return depth; + if(has_attributes) { + return depth; - } else if(depth < context) { - xml_child_iter(xml_node, child, - if(in_upper_context(depth+1, context, child)) { - return depth; - } - ); + } else if(depth < context) { + xmlNode *child = NULL; + for(child = xml_node; child != NULL; child = child->next) { + if(in_upper_context(depth+1, context, child)) { + return depth; + } } - return 0; + } + return 0; } xmlNode * subtract_xml_object(xmlNode *parent, xmlNode *left, xmlNode *right, gboolean full, const char *marker) { - gboolean skip = FALSE; - gboolean differences = FALSE; - xmlNode *diff = NULL; - xmlNode *child_diff = NULL; - xmlNode *right_child = NULL; + gboolean skip = FALSE; + gboolean differences = FALSE; + xmlNode *diff = NULL; + xmlNode *child_diff = NULL; + xmlNode *right_child = NULL; + xmlNode *left_child = NULL; - const char *id = NULL; - const char *name = NULL; - const char *value = NULL; - const char *right_val = NULL; + const char *id = NULL; + const char *name = NULL; + const char *value = NULL; + const char *right_val = NULL; - int lpc = 0; - static int filter_len = DIMOF(filter); + int lpc = 0; + static int filter_len = DIMOF(filter); - if(left == NULL) { - return NULL; - } + if(left == NULL) { + return NULL; + } - id = ID(left); - if(right == NULL) { - xmlNode *deleted = NULL; + id = ID(left); + if(right == NULL) { + xmlNode *deleted = NULL; - crm_debug_5("Processing <%s id=%s> (complete copy)", - crm_element_name(left), id); - deleted = add_node_copy(parent, left); - crm_xml_add(deleted, XML_DIFF_MARKER, marker); + crm_debug_5("Processing <%s id=%s> (complete copy)", + crm_element_name(left), id); + deleted = add_node_copy(parent, left); + crm_xml_add(deleted, XML_DIFF_MARKER, marker); - return deleted; - } + return deleted; + } - name = crm_element_name(left); - CRM_CHECK(name != NULL, return NULL); + name = crm_element_name(left); + CRM_CHECK(name != NULL, return NULL); - /* Avoiding creating the full heirarchy would save even more work here */ - diff = create_xml_node(parent, name); + /* Avoiding creating the full heirarchy would save even more work here */ + diff = create_xml_node(parent, name); - /* Reset filter */ - for(lpc = 0; lpc < filter_len; lpc++){ - filter[lpc].found = FALSE; - } + /* Reset filter */ + for(lpc = 0; lpc < filter_len; lpc++){ + filter[lpc].found = FALSE; + } - /* changes to child objects */ - xml_child_iter( - left, left_child, - right_child = find_entity( - right, crm_element_name(left_child), ID(left_child)); - child_diff = subtract_xml_object(diff, left_child, right_child, full, marker); - if(child_diff != NULL) { - differences = TRUE; - } - ); + /* changes to child objects */ + for(left_child = left; left_child != NULL; left_child = left_child->next) { + right_child = find_entity( + right, crm_element_name(left_child), ID(left_child)); + child_diff = subtract_xml_object(diff, left_child, right_child, full, marker); + if(child_diff != NULL) { + differences = TRUE; + } + } - if(differences == FALSE) { - /* check for XML_DIFF_MARKER in a child */ - xml_child_iter( - right, right_child, - value = crm_element_value(right_child, XML_DIFF_MARKER); - if(value != NULL && safe_str_eq(value, "removed:top")) { - crm_debug_3("Found the root of the deletion: %s", name); - xml_prop_iter(left, name, value, xmlSetProp(diff, (const xmlChar*)name, (const xmlChar*)value)); - differences = TRUE; - goto done; - } - ); - - } else if(full) { - xml_prop_iter(left, name, value, xmlSetProp(diff, (const xmlChar*)name, (const xmlChar*)value)); - - /* We already have everything we need... */ - goto done; - - } else if(id) { - xmlSetProp(diff, (const xmlChar*)XML_ATTR_ID, (const xmlChar*)id); + if(differences == FALSE) { + /* check for XML_DIFF_MARKER in a child */ + for(right_child = right; right_child != NULL; right_child = right_child->next) { + value = crm_element_value(right_child, XML_DIFF_MARKER); + if(value != NULL && safe_str_eq(value, "removed:top")) { + crm_debug_3("Found the root of the deletion: %s", name); + xml_prop_iter(left, name, value, xmlSetProp(diff, (const xmlChar*)name, (const xmlChar*)value)); + differences = TRUE; + goto done; + } } + + } else if(full) { + xml_prop_iter(left, name, value, xmlSetProp(diff, (const xmlChar*)name, (const xmlChar*)value)); + + /* We already have everything we need... */ + goto done; + + } else if(id) { + xmlSetProp(diff, (const xmlChar*)XML_ATTR_ID, (const xmlChar*)id); + } - /* changes to name/value pairs */ - xml_prop_name_iter( - left, prop_name, + /* changes to name/value pairs */ + xml_prop_name_iter( + left, prop_name, - if(crm_str_eq(prop_name, XML_ATTR_ID, TRUE)) { - continue; - } + if(crm_str_eq(prop_name, XML_ATTR_ID, TRUE)) { + continue; + } - skip = FALSE; - for(lpc = 0; skip == FALSE && lpc < filter_len; lpc++){ - if(filter[lpc].found == FALSE && crm_str_eq(prop_name, filter[lpc].string, TRUE)) { - filter[lpc].found = TRUE; - skip = TRUE; - break; - } + skip = FALSE; + for(lpc = 0; skip == FALSE && lpc < filter_len; lpc++){ + if(filter[lpc].found == FALSE && crm_str_eq(prop_name, filter[lpc].string, TRUE)) { + filter[lpc].found = TRUE; + skip = TRUE; + break; } + } - if(skip) { continue; } + if(skip) { continue; } - right_val = crm_element_value(right, prop_name); - if(right_val == NULL) { - /* new */ + right_val = crm_element_value(right, prop_name); + if(right_val == NULL) { + /* new */ + differences = TRUE; + if(full) { + xml_prop_iter(left, name, value, xmlSetProp(diff, (const xmlChar*)name, (const xmlChar*)value)); + break; + + } else { + const char *left_value = crm_element_value(left, prop_name); + xmlSetProp(diff, (const xmlChar*)prop_name, (const xmlChar*)value); + crm_xml_add(diff, prop_name, left_value); + } + + } else { + /* Only now do we need the left value */ + const char *left_value = crm_element_value(left, prop_name); + if(strcmp(left_value, right_val) == 0) { + /* unchanged */ + + } else { + /* changed */ differences = TRUE; if(full) { xml_prop_iter(left, name, value, xmlSetProp(diff, (const xmlChar*)name, (const xmlChar*)value)); break; - + } else { - const char *left_value = crm_element_value(left, prop_name); - xmlSetProp(diff, (const xmlChar*)prop_name, (const xmlChar*)value); crm_xml_add(diff, prop_name, left_value); } - - } else { - /* Only now do we need the left value */ - const char *left_value = crm_element_value(left, prop_name); - if(strcmp(left_value, right_val) == 0) { - /* unchanged */ - - } else { - /* changed */ - differences = TRUE; - if(full) { - xml_prop_iter(left, name, value, xmlSetProp(diff, (const xmlChar*)name, (const xmlChar*)value)); - break; - - } else { - crm_xml_add(diff, prop_name, left_value); - } - } } + } - ); + ); - if(differences == FALSE) { - free_xml_from_parent(parent, diff); - crm_debug_5("\tNo changes to <%s id=%s>", crm_str(name), id); - return NULL; + if(differences == FALSE) { + free_xml_from_parent(parent, diff); + crm_debug_5("\tNo changes to <%s id=%s>", crm_str(name), id); + return NULL; - } else if(full == FALSE && id) { - crm_xml_add(diff, XML_ATTR_ID, id); - } + } else if(full == FALSE && id) { + crm_xml_add(diff, XML_ATTR_ID, id); + } done: - return diff; + return diff; } int add_xml_object(xmlNode *parent, xmlNode *target, xmlNode *update, gboolean as_diff) { - const char *object_id = NULL; - const char *object_name = NULL; + xmlNode *a_child = NULL; + const char *object_id = NULL; + const char *object_name = NULL; #if XML_PARSE_DEBUG - crm_log_xml(LOG_DEBUG_5, "update:", update); - crm_log_xml(LOG_DEBUG_5, "target:", target); + crm_log_xml(LOG_DEBUG_5, "update:", update); + crm_log_xml(LOG_DEBUG_5, "target:", target); #endif - CRM_CHECK(update != NULL, return 0); + CRM_CHECK(update != NULL, return 0); - object_name = crm_element_name(update); - object_id = ID(update); + object_name = crm_element_name(update); + object_id = ID(update); - CRM_CHECK(object_name != NULL, return 0); + CRM_CHECK(object_name != NULL, return 0); - if(target == NULL && object_id == NULL) { - /* placeholder object */ - target = find_xml_node(parent, object_name, FALSE); + if(target == NULL && object_id == NULL) { + /* placeholder object */ + target = find_xml_node(parent, object_name, FALSE); - } else if(target == NULL) { - target = find_entity(parent, object_name, object_id); - } + } else if(target == NULL) { + target = find_entity(parent, object_name, object_id); + } - if(target == NULL) { - target = create_xml_node(parent, object_name); - CRM_CHECK(target != NULL, return 0); + if(target == NULL) { + target = create_xml_node(parent, object_name); + CRM_CHECK(target != NULL, return 0); #if XML_PARSER_DEBUG - crm_debug_2("Added <%s%s%s/>", crm_str(object_name), - object_id?" id=":"", object_id?object_id:""); + crm_debug_2("Added <%s%s%s/>", crm_str(object_name), + object_id?" id=":"", object_id?object_id:""); - } else { - crm_debug_3("Found node <%s%s%s/> to update", - crm_str(object_name), - object_id?" id=":"", object_id?object_id:""); + } else { + crm_debug_3("Found node <%s%s%s/> to update", + crm_str(object_name), + object_id?" id=":"", object_id?object_id:""); #endif - } + } - if(as_diff == FALSE) { - /* So that expand_plus_plus() gets called */ - copy_in_properties(target, update); + if(as_diff == FALSE) { + /* So that expand_plus_plus() gets called */ + copy_in_properties(target, update); - } else { - /* No need for expand_plus_plus(), just raw speed */ - xml_prop_iter(update, p_name, p_value, - /* Remove it first so the ordering of the update is preserved */ - xmlUnsetProp(target, (const xmlChar*)p_name); - xmlSetProp(target, (const xmlChar*)p_name, (const xmlChar*)p_value)); - } + } else { + /* No need for expand_plus_plus(), just raw speed */ + xml_prop_iter(update, p_name, p_value, + /* Remove it first so the ordering of the update is preserved */ + xmlUnsetProp(target, (const xmlChar*)p_name); + xmlSetProp(target, (const xmlChar*)p_name, (const xmlChar*)p_value)); + } - xml_child_iter( - update, a_child, + for(a_child = update; a_child != NULL; a_child = a_child->next) { #if XML_PARSER_DEBUG - crm_debug_4("Updating child <%s id=%s>", - crm_element_name(a_child), ID(a_child)); + crm_debug_4("Updating child <%s id=%s>", + crm_element_name(a_child), ID(a_child)); #endif - add_xml_object(target, NULL, a_child, as_diff); - ); + add_xml_object(target, NULL, a_child, as_diff); + } #if XML_PARSER_DEBUG - crm_debug_3("Finished with <%s id=%s>", - crm_str(object_name), crm_str(object_id)); + crm_debug_3("Finished with <%s id=%s>", + crm_str(object_name), crm_str(object_id)); #endif - return 0; + return 0; } gboolean update_xml_child(xmlNode *child, xmlNode *to_update) { - gboolean can_update = TRUE; + gboolean can_update = TRUE; + xmlNode *child_of_child = NULL; - CRM_CHECK(child != NULL, return FALSE); - CRM_CHECK(to_update != NULL, return FALSE); + CRM_CHECK(child != NULL, return FALSE); + CRM_CHECK(to_update != NULL, return FALSE); - if(safe_str_neq(crm_element_name(to_update), crm_element_name(child))) { - can_update = FALSE; + if(safe_str_neq(crm_element_name(to_update), crm_element_name(child))) { + can_update = FALSE; - } else if(safe_str_neq(ID(to_update), ID(child))) { - can_update = FALSE; + } else if(safe_str_neq(ID(to_update), ID(child))) { + can_update = FALSE; - } else if(can_update) { + } else if(can_update) { #if XML_PARSER_DEBUG - crm_log_xml_debug_2(child, "Update match found..."); + crm_log_xml_debug_2(child, "Update match found..."); #endif - add_xml_object(NULL, child, to_update, FALSE); - } + add_xml_object(NULL, child, to_update, FALSE); + } - xml_child_iter( - child, child_of_child, - /* only update the first one */ - if(can_update) { - break; - } - can_update = update_xml_child(child_of_child, to_update); - ); + for(child_of_child = child; child_of_child != NULL; child_of_child = child_of_child->next) { + /* only update the first one */ + if(can_update) { + break; + } + can_update = update_xml_child(child_of_child, to_update); + } - return can_update; + return can_update; } int find_xml_children(xmlNode **children, xmlNode *root, const char *tag, const char *field, const char *value, gboolean search_matches) { - int match_found = 0; + int match_found = 0; - CRM_CHECK(root != NULL, return FALSE); - CRM_CHECK(children != NULL, return FALSE); + CRM_CHECK(root != NULL, return FALSE); + CRM_CHECK(children != NULL, return FALSE); - if(tag != NULL && safe_str_neq(tag, crm_element_name(root))) { + if(tag != NULL && safe_str_neq(tag, crm_element_name(root))) { - } else if(value != NULL - && safe_str_neq(value, crm_element_value(root, field))) { + } else if(value != NULL + && safe_str_neq(value, crm_element_value(root, field))) { - } else { - if(*children == NULL) { - *children = create_xml_node(NULL, __FUNCTION__); - } - add_node_copy(*children, root); - match_found = 1; + } else { + if(*children == NULL) { + *children = create_xml_node(NULL, __FUNCTION__); } + add_node_copy(*children, root); + match_found = 1; + } - if(search_matches || match_found == 0) { - xml_child_iter( - root, child, - match_found += find_xml_children( - children, child, tag, field, value, - search_matches); - ); + if(search_matches || match_found == 0) { + xmlNode *child = NULL; + for(child = root; child != NULL; child = child->next) { + match_found += find_xml_children( + children, child, tag, field, value, + search_matches); } + } - return match_found; + return match_found; } gboolean replace_xml_child(xmlNode *parent, xmlNode *child, xmlNode *update, gboolean delete_only) { - gboolean can_delete = FALSE; + gboolean can_delete = FALSE; + xmlNode *child_of_child = NULL; - const char *up_id = NULL; - const char *child_id = NULL; - const char *right_val = NULL; + const char *up_id = NULL; + const char *child_id = NULL; + const char *right_val = NULL; - CRM_CHECK(child != NULL, return FALSE); - CRM_CHECK(update != NULL, return FALSE); + CRM_CHECK(child != NULL, return FALSE); + CRM_CHECK(update != NULL, return FALSE); - up_id = ID(update); - child_id = ID(child); + up_id = ID(update); + child_id = ID(child); - if(up_id == NULL || safe_str_eq(child_id, up_id)) { - can_delete = TRUE; - } - if(safe_str_neq(crm_element_name(update), crm_element_name(child))) { - can_delete = FALSE; - } - if(can_delete && delete_only) { - xml_prop_iter(update, prop_name, left_value, - right_val = crm_element_value(child, prop_name); - if(safe_str_neq(left_value, right_val)) { - can_delete = FALSE; - } - ); - } + if(up_id == NULL || safe_str_eq(child_id, up_id)) { + can_delete = TRUE; + } + if(safe_str_neq(crm_element_name(update), crm_element_name(child))) { + can_delete = FALSE; + } + if(can_delete && delete_only) { + xml_prop_iter(update, prop_name, left_value, + right_val = crm_element_value(child, prop_name); + if(safe_str_neq(left_value, right_val)) { + can_delete = FALSE; + } + ); + } - if(can_delete && parent != NULL) { - crm_log_xml_debug_4(child, "Delete match found..."); - if(delete_only || update == NULL) { - free_xml_from_parent(NULL, child); + if(can_delete && parent != NULL) { + crm_log_xml_debug_4(child, "Delete match found..."); + if(delete_only || update == NULL) { + free_xml_from_parent(NULL, child); - } else { - xmlNode *tmp = copy_xml(update); - xmlDoc *doc = tmp->doc; - xmlNode *old = xmlReplaceNode(child, tmp); - free_xml_from_parent(NULL, old); - xmlDocSetRootElement(doc, NULL); - xmlFreeDoc(doc); - } - child = NULL; - return TRUE; + } else { + xmlNode *tmp = copy_xml(update); + xmlDoc *doc = tmp->doc; + xmlNode *old = xmlReplaceNode(child, tmp); + free_xml_from_parent(NULL, old); + xmlDocSetRootElement(doc, NULL); + xmlFreeDoc(doc); + } + child = NULL; + return TRUE; - } else if(can_delete) { - crm_log_xml_debug(child, "Cannot delete the search root"); - can_delete = FALSE; - } + } else if(can_delete) { + crm_log_xml_debug(child, "Cannot delete the search root"); + can_delete = FALSE; + } - xml_child_iter( - child, child_of_child, - /* only delete the first one */ - if(can_delete) { - break; - } - can_delete = replace_xml_child(child, child_of_child, update, delete_only); - ); + for(child_of_child = child; child_of_child != NULL; child_of_child = child_of_child->next) { + /* only delete the first one */ + if(can_delete) { + break; + } + can_delete = replace_xml_child(child, child_of_child, update, delete_only); + } - return can_delete; + return can_delete; } void hash2nvpair(gpointer key, gpointer value, gpointer user_data) { - const char *name = key; - const char *s_value = value; + const char *name = key; + const char *s_value = value; - xmlNode *xml_node = user_data; - xmlNode *xml_child = create_xml_node(xml_node, XML_CIB_TAG_NVPAIR); + xmlNode *xml_node = user_data; + xmlNode *xml_child = create_xml_node(xml_node, XML_CIB_TAG_NVPAIR); - crm_xml_add(xml_child, XML_ATTR_ID, name); - crm_xml_add(xml_child, XML_NVPAIR_ATTR_NAME, name); - crm_xml_add(xml_child, XML_NVPAIR_ATTR_VALUE, s_value); + crm_xml_add(xml_child, XML_ATTR_ID, name); + crm_xml_add(xml_child, XML_NVPAIR_ATTR_NAME, name); + crm_xml_add(xml_child, XML_NVPAIR_ATTR_VALUE, s_value); - crm_debug_3("dumped: name=%s value=%s", name, s_value); + crm_debug_3("dumped: name=%s value=%s", name, s_value); } void hash2smartfield(gpointer key, gpointer value, gpointer user_data) { - const char *name = key; - const char *s_value = value; + const char *name = key; + const char *s_value = value; - xmlNode *xml_node = user_data; + xmlNode *xml_node = user_data; - if(isdigit(name[0])) { - xmlNode *tmp = create_xml_node(xml_node, XML_TAG_PARAM); - crm_xml_add(tmp, XML_NVPAIR_ATTR_NAME, name); - crm_xml_add(tmp, XML_NVPAIR_ATTR_VALUE, s_value); + if(isdigit(name[0])) { + xmlNode *tmp = create_xml_node(xml_node, XML_TAG_PARAM); + crm_xml_add(tmp, XML_NVPAIR_ATTR_NAME, name); + crm_xml_add(tmp, XML_NVPAIR_ATTR_VALUE, s_value); - } else if(crm_element_value(xml_node, name) == NULL) { - crm_xml_add(xml_node, name, s_value); - crm_debug_3("dumped: %s=%s", name, s_value); + } else if(crm_element_value(xml_node, name) == NULL) { + crm_xml_add(xml_node, name, s_value); + crm_debug_3("dumped: %s=%s", name, s_value); - } else { - crm_debug_2("duplicate: %s=%s", name, s_value); - } + } else { + crm_debug_2("duplicate: %s=%s", name, s_value); + } } void hash2field(gpointer key, gpointer value, gpointer user_data) { - const char *name = key; - const char *s_value = value; + const char *name = key; + const char *s_value = value; - xmlNode *xml_node = user_data; + xmlNode *xml_node = user_data; - if(crm_element_value(xml_node, name) == NULL) { - crm_xml_add(xml_node, name, s_value); - crm_debug_3("dumped: %s=%s", name, s_value); + if(crm_element_value(xml_node, name) == NULL) { + crm_xml_add(xml_node, name, s_value); + crm_debug_3("dumped: %s=%s", name, s_value); - } else { - crm_debug_2("duplicate: %s=%s", name, s_value); - } + } else { + crm_debug_2("duplicate: %s=%s", name, s_value); + } } void hash2metafield(gpointer key, gpointer value, gpointer user_data) { char *crm_name = NULL; if(key == NULL || value == NULL) { return; } else if(((char*)key)[0] == '#') { return; } else if(strstr(key, ":")) { return; } crm_name = crm_meta_name(key); hash2field(crm_name, value, user_data); crm_free(crm_name); } GHashTable * xml2list(xmlNode *parent) { - xmlNode *nvpair_list = NULL; - GHashTable *nvpair_hash = g_hash_table_new_full( - g_str_hash, g_str_equal, - g_hash_destroy_str, g_hash_destroy_str); + xmlNode *child = NULL; + xmlNode *nvpair_list = NULL; + GHashTable *nvpair_hash = g_hash_table_new_full( + g_str_hash, g_str_equal, + g_hash_destroy_str, g_hash_destroy_str); - CRM_CHECK(parent != NULL, return nvpair_hash); + CRM_CHECK(parent != NULL, return nvpair_hash); - nvpair_list = find_xml_node(parent, XML_TAG_ATTRS, FALSE); - if(nvpair_list == NULL) { - crm_debug_2("No attributes in %s", - crm_element_name(parent)); - crm_log_xml_debug_2( - parent,"No attributes for resource op"); - } + nvpair_list = find_xml_node(parent, XML_TAG_ATTRS, FALSE); + if(nvpair_list == NULL) { + crm_debug_2("No attributes in %s", + crm_element_name(parent)); + crm_log_xml_debug_2( + parent,"No attributes for resource op"); + } - crm_log_xml_debug_3(nvpair_list, "Unpacking"); + crm_log_xml_debug_3(nvpair_list, "Unpacking"); - xml_prop_iter( - nvpair_list, key, value, + xml_prop_iter( + nvpair_list, key, value, - crm_debug_4("Added %s=%s", key, value); + crm_debug_4("Added %s=%s", key, value); - g_hash_table_insert( - nvpair_hash, crm_strdup(key), crm_strdup(value)); - ); - - xml_child_iter_filter( - nvpair_list, child, XML_TAG_PARAM, + g_hash_table_insert( + nvpair_hash, crm_strdup(key), crm_strdup(value)); + ); + for(child = nvpair_list; child != NULL; child = child->next) { + if(crm_str_eq((const char *)child->name, XML_TAG_PARAM, TRUE)) { const char *key = crm_element_value(child, XML_NVPAIR_ATTR_NAME); const char *value = crm_element_value(child, XML_NVPAIR_ATTR_VALUE); crm_debug_4("Added %s=%s", key, value); if(key != NULL && value != NULL) { g_hash_table_insert(nvpair_hash, crm_strdup(key), crm_strdup(value)); } - ); + } + } - return nvpair_hash; + return nvpair_hash; } typedef struct name_value_s { const char *name; const void *value; } name_value_t; static gint sort_pairs(gconstpointer a, gconstpointer b) { int rc = 0; - const name_value_t *pair_a = a; - const name_value_t *pair_b = b; + const name_value_t *pair_a = a; + const name_value_t *pair_b = b; - CRM_ASSERT(a != NULL); - CRM_ASSERT(pair_a->name != NULL); + CRM_ASSERT(a != NULL); + CRM_ASSERT(pair_a->name != NULL); - CRM_ASSERT(b != NULL); - CRM_ASSERT(pair_b->name != NULL); + CRM_ASSERT(b != NULL); + CRM_ASSERT(pair_b->name != NULL); - rc = strcmp(pair_a->name, pair_b->name); - if(rc < 0) { - return -1; - } else if(rc > 0) { - return 1; - } - return 0; + rc = strcmp(pair_a->name, pair_b->name); + if(rc < 0) { + return -1; + } else if(rc > 0) { + return 1; + } + return 0; } static void dump_pair(gpointer data, gpointer user_data) { - name_value_t *pair = data; - xmlNode *parent = user_data; - crm_xml_add(parent, pair->name, pair->value); + name_value_t *pair = data; + xmlNode *parent = user_data; + crm_xml_add(parent, pair->name, pair->value); } xmlNode * sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive) { - GListPtr sorted = NULL; - GListPtr unsorted = NULL; - name_value_t *pair = NULL; - xmlNode *result = NULL; - const char *name = NULL; + xmlNode *child = NULL; + GListPtr sorted = NULL; + GListPtr unsorted = NULL; + name_value_t *pair = NULL; + xmlNode *result = NULL; + const char *name = NULL; - CRM_CHECK(input != NULL, return NULL); + CRM_CHECK(input != NULL, return NULL); - name = crm_element_name(input); - CRM_CHECK(name != NULL, return NULL); + name = crm_element_name(input); + CRM_CHECK(name != NULL, return NULL); - result = create_xml_node(parent, name); + result = create_xml_node(parent, name); - xml_prop_iter(input, p_name, p_value, - crm_malloc0(pair, sizeof(name_value_t)); - pair->name = p_name; - pair->value = p_value; - unsorted = g_list_prepend(unsorted, pair); - pair = NULL; - ); + xml_prop_iter(input, p_name, p_value, + crm_malloc0(pair, sizeof(name_value_t)); + pair->name = p_name; + pair->value = p_value; + unsorted = g_list_prepend(unsorted, pair); + pair = NULL; + ); - sorted = g_list_sort(unsorted, sort_pairs); - g_list_foreach(sorted, dump_pair, result); - slist_destroy(name_value_t, child, sorted, crm_free(child)); + sorted = g_list_sort(unsorted, sort_pairs); + g_list_foreach(sorted, dump_pair, result); + slist_basic_destroy(sorted); + for(child = input; child != NULL; child = child->next) { if(recursive) { - xml_child_iter(input, child, sorted_xml(child, result, recursive)); + sorted_xml(child, result, recursive); } else { - xml_child_iter(input, child, add_node_copy(result, child)); + add_node_copy(result, child); } + } - return result; + return result; } static void filter_xml(xmlNode *data, filter_t *filter, int filter_len, gboolean recursive) { int lpc = 0; + xmlNode *child = NULL; for(lpc = 0; lpc < filter_len; lpc++) { xml_remove_prop(data, filter[lpc].string); } if(recursive == FALSE || filter_len == 0) { return; } - xml_child_iter(data, child, filter_xml(child, filter, filter_len, recursive)); + for(child = data; child != NULL; child = child->next) { + filter_xml(child, filter, filter_len, recursive); + } } /* "c048eae664dba840e1d2060f00299e9d" */ static char * calculate_xml_digest_v1(xmlNode *input, gboolean sort, gboolean do_filter) { - int i = 0; - int digest_len = 16; - char *digest = NULL; - unsigned char *raw_digest = NULL; - xmlNode *copy = NULL; - char *buffer = NULL; - size_t buffer_len = 0; - - if(sort || do_filter) { - copy = sorted_xml(input, NULL, TRUE); - input = copy; - } + int i = 0; + int digest_len = 16; + char *digest = NULL; + unsigned char *raw_digest = NULL; + xmlNode *copy = NULL; + char *buffer = NULL; + size_t buffer_len = 0; - if(do_filter) { - filter_xml(input, filter, DIMOF(filter), TRUE); - } + if(sort || do_filter) { + copy = sorted_xml(input, NULL, TRUE); + input = copy; + } + + if(do_filter) { + filter_xml(input, filter, DIMOF(filter), TRUE); + } - buffer = dump_xml(input, FALSE, TRUE); - buffer_len = strlen(buffer); + buffer = dump_xml(input, FALSE, TRUE); + buffer_len = strlen(buffer); - CRM_CHECK(buffer != NULL && buffer_len > 0, free_xml(copy); crm_free(buffer); return NULL); + CRM_CHECK(buffer != NULL && buffer_len > 0, free_xml(copy); crm_free(buffer); return NULL); - crm_malloc(digest, (2 * digest_len + 1)); - crm_malloc(raw_digest, (digest_len + 1)); - MD5((unsigned char *)buffer, buffer_len, raw_digest); - for(i = 0; i < digest_len; i++) { - sprintf(digest+(2*i), "%02x", raw_digest[i]); - } - digest[(2*digest_len)] = 0; - crm_debug_2("Digest %s: %s\n", digest, buffer); - crm_log_xml(LOG_DEBUG_3, "digest:source", copy); - crm_free(buffer); - crm_free(raw_digest); - free_xml(copy); - return digest; + crm_malloc(digest, (2 * digest_len + 1)); + crm_malloc(raw_digest, (digest_len + 1)); + MD5((unsigned char *)buffer, buffer_len, raw_digest); + for(i = 0; i < digest_len; i++) { + sprintf(digest+(2*i), "%02x", raw_digest[i]); + } + digest[(2*digest_len)] = 0; + crm_debug_2("Digest %s: %s\n", digest, buffer); + crm_log_xml(LOG_DEBUG_3, "digest:source", copy); + crm_free(buffer); + crm_free(raw_digest); + free_xml(copy); + return digest; } static char * calculate_xml_digest_v2(xmlNode *input, gboolean do_filter) { int i = 0; int digest_len = 16; char *digest = NULL; size_t buffer_len = 0; int filter_size = DIMOF(filter); unsigned char *raw_digest = NULL; xmlDoc *doc = NULL; xmlNode *copy = NULL; xmlBuffer *xml_buffer = NULL; if(do_filter && BEST_EFFORT_STATUS) { /* Exclude the status calculation from the digest * * This doesn't mean it wont be sync'd, we just wont be paranoid * about it being an _exact_ copy * * We don't need it to be exact, since we throw it away and regenerate * from our peers whenever a new DC is elected anyway * * Importantly, this reduces the amount of XML to copy+export as * well as the amount of data for MD5 needs to operate on */ + xmlNode *child = NULL; copy = create_xml_node(NULL, XML_TAG_CIB); xml_prop_iter(input, p_name, p_value, xmlSetProp(copy, (const xmlChar*)p_name, (const xmlChar*)p_value)); xml_remove_prop(copy, XML_ATTR_ORIGIN); xml_remove_prop(copy, XML_CIB_ATTR_WRITTEN); /* We just did all the filtering */ - xml_child_iter(input, child, + for(child = input; child != NULL; child = child->next) { if(safe_str_neq(crm_element_name(child), XML_CIB_TAG_STATUS)) { add_node_copy(copy, child); } - ); + } } else if(do_filter) { copy = copy_xml(input); filter_xml(copy, filter, filter_size, TRUE); input = copy; } doc = getDocPtr(input); xml_buffer = xmlBufferCreate(); CRM_ASSERT(xml_buffer != NULL); CRM_CHECK(doc != NULL, return NULL); /* doc will only be NULL if an_xml_node is */ buffer_len = xmlNodeDump(xml_buffer, doc, input, 0, FALSE); CRM_CHECK(xml_buffer->content != NULL && buffer_len > 0, goto done); crm_malloc(digest, (2 * digest_len + 1)); crm_malloc(raw_digest, (digest_len + 1)); MD5((unsigned char *)xml_buffer->content, buffer_len, raw_digest); for(i = 0; i < digest_len; i++) { sprintf(digest+(2*i), "%02x", raw_digest[i]); } digest[(2*digest_len)] = 0; crm_trace("Digest %s\n", digest); crm_log_xml_trace(input, "digest:source"); done: xmlBufferFree(xml_buffer); crm_free(raw_digest); free_xml(copy); return digest; } char * calculate_on_disk_digest(xmlNode *input) { /* Always use the v1 format for on-disk digests * a) its a compatability nightmare * b) we only use this once at startup, all other * invocations are in a separate child process */ return calculate_xml_digest_v1(input, FALSE, FALSE); } char * calculate_operation_digest(xmlNode *input, const char *version) { /* We still need the sorting for parameter digests */ return calculate_xml_digest_v1(input, TRUE, FALSE); } char * calculate_xml_digest(xmlNode *input, gboolean sort, gboolean do_filter) { return calculate_xml_digest_v1(input, sort, do_filter); } char * calculate_xml_versioned_digest(xmlNode *input, gboolean sort, gboolean do_filter, const char *version) { /* * The sorting associated with v1 digest creation accounted for 23% of * the CIB's CPU usage on the server. v2 drops this. * * The filtering accounts for an additional 2.5% and we may want to * remove it in future. * * v2 also uses the xmlBuffer contents directly to avoid additional copying */ if(version == NULL || compare_version("3.0.5", version) > 0) { crm_trace("Using v1 digest algorithm for %s", crm_str(version)); return calculate_xml_digest_v1(input, sort, do_filter); } crm_trace("Using v2 digest algorithm for %s", crm_str(version)); return calculate_xml_digest_v2(input, do_filter); } #if HAVE_LIBXML2 # include # include # include # include # include #endif static gboolean validate_with_dtd( - xmlDocPtr doc, gboolean to_logs, const char *dtd_file) + xmlDocPtr doc, gboolean to_logs, const char *dtd_file) { - gboolean valid = TRUE; + gboolean valid = TRUE; - xmlDtdPtr dtd = NULL; - xmlValidCtxtPtr cvp = NULL; + xmlDtdPtr dtd = NULL; + xmlValidCtxtPtr cvp = NULL; - CRM_CHECK(doc != NULL, return FALSE); - CRM_CHECK(dtd_file != NULL, return FALSE); + CRM_CHECK(doc != NULL, return FALSE); + CRM_CHECK(dtd_file != NULL, return FALSE); - dtd = xmlParseDTD(NULL, (const xmlChar *)dtd_file); - CRM_CHECK(dtd != NULL, crm_err("Could not find/parse %s", dtd_file); goto cleanup); + dtd = xmlParseDTD(NULL, (const xmlChar *)dtd_file); + CRM_CHECK(dtd != NULL, crm_err("Could not find/parse %s", dtd_file); goto cleanup); - cvp = xmlNewValidCtxt(); - CRM_CHECK(cvp != NULL, goto cleanup); + cvp = xmlNewValidCtxt(); + CRM_CHECK(cvp != NULL, goto cleanup); - if(to_logs) { - cvp->userData = (void *) LOG_ERR; - cvp->error = (xmlValidityErrorFunc) cl_log; - cvp->warning = (xmlValidityWarningFunc) cl_log; - } else { - cvp->userData = (void *) stderr; - cvp->error = (xmlValidityErrorFunc) fprintf; - cvp->warning = (xmlValidityWarningFunc) fprintf; - } + if(to_logs) { + cvp->userData = (void *) LOG_ERR; + cvp->error = (xmlValidityErrorFunc) cl_log; + cvp->warning = (xmlValidityWarningFunc) cl_log; + } else { + cvp->userData = (void *) stderr; + cvp->error = (xmlValidityErrorFunc) fprintf; + cvp->warning = (xmlValidityWarningFunc) fprintf; + } - if (!xmlValidateDtd(cvp, doc, dtd)) { - valid = FALSE; - } + if (!xmlValidateDtd(cvp, doc, dtd)) { + valid = FALSE; + } cleanup: - if(cvp) { - xmlFreeValidCtxt(cvp); - } - if(dtd) { - xmlFreeDtd(dtd); - } + if(cvp) { + xmlFreeValidCtxt(cvp); + } + if(dtd) { + xmlFreeDtd(dtd); + } - return valid; + return valid; } xmlNode *first_named_child(xmlNode *parent, const char *name) { - xml_child_iter_filter(parent, match, name, return match); + xmlNode *match = NULL; + for(match = parent; match != NULL; match = match->next) { + if(crm_str_eq((const char*)match->name, name, TRUE)) { + return match; + } + } return NULL; } #if 0 static void relaxng_invalid_stderr(void * userData, xmlErrorPtr error) { /* -Structure xmlError -struct _xmlError { - int domain : What part of the library raised this er - int code : The error code, e.g. an xmlParserError - char * message : human-readable informative error messag - xmlErrorLevel level : how consequent is the error - char * file : the filename - int line : the line number if available - char * str1 : extra string information - char * str2 : extra string information - char * str3 : extra string information - int int1 : extra number information - int int2 : column number of the error or 0 if N/A - void * ctxt : the parser context if available - void * node : the node in the tree -} - */ + Structure xmlError + struct _xmlError { + int domain : What part of the library raised this er + int code : The error code, e.g. an xmlParserError + char * message : human-readable informative error messag + xmlErrorLevel level : how consequent is the error + char * file : the filename + int line : the line number if available + char * str1 : extra string information + char * str2 : extra string information + char * str3 : extra string information + int int1 : extra number information + int int2 : column number of the error or 0 if N/A + void * ctxt : the parser context if available + void * node : the node in the tree + } + */ crm_err("Structured error: line=%d, level=%d %s", error->line, error->level, error->message); } #endif static gboolean validate_with_relaxng( xmlDocPtr doc, gboolean to_logs, const char *relaxng_file, relaxng_ctx_cache_t **cached_ctx) { int rc = 0; gboolean valid = TRUE; relaxng_ctx_cache_t *ctx = NULL; CRM_CHECK(doc != NULL, return FALSE); CRM_CHECK(relaxng_file != NULL, return FALSE); if(cached_ctx && *cached_ctx) { ctx = *cached_ctx; } else { crm_info("Creating RNG parser context"); crm_malloc0(ctx, sizeof(relaxng_ctx_cache_t)); xmlLoadExtDtdDefaultValue = 1; ctx->parser = xmlRelaxNGNewParserCtxt(relaxng_file); CRM_CHECK(ctx->parser != NULL, goto cleanup); if(to_logs) { xmlRelaxNGSetParserErrors(ctx->parser, (xmlRelaxNGValidityErrorFunc) cl_log, (xmlRelaxNGValidityWarningFunc) cl_log, GUINT_TO_POINTER(LOG_ERR)); } else { xmlRelaxNGSetParserErrors(ctx->parser, (xmlRelaxNGValidityErrorFunc) fprintf, (xmlRelaxNGValidityWarningFunc) fprintf, stderr); } ctx->rng = xmlRelaxNGParse(ctx->parser); CRM_CHECK(ctx->rng != NULL, crm_err("Could not find/parse %s", relaxng_file); goto cleanup); ctx->valid = xmlRelaxNGNewValidCtxt(ctx->rng); CRM_CHECK(ctx->valid != NULL, goto cleanup); if(to_logs) { xmlRelaxNGSetValidErrors(ctx->valid, (xmlRelaxNGValidityErrorFunc) cl_log, (xmlRelaxNGValidityWarningFunc) cl_log, GUINT_TO_POINTER(LOG_ERR)); } else { xmlRelaxNGSetValidErrors(ctx->valid, (xmlRelaxNGValidityErrorFunc) fprintf, (xmlRelaxNGValidityWarningFunc) fprintf, stderr); } } /* xmlRelaxNGSetValidStructuredErrors( */ /* valid, relaxng_invalid_stderr, valid); */ xmlLineNumbersDefault(1); rc = xmlRelaxNGValidateDoc(ctx->valid, doc); if (rc > 0) { valid = FALSE; } else if (rc < 0) { crm_err("Internal libxml error during validation\n"); } cleanup: if(cached_ctx) { *cached_ctx = ctx; } else { if(ctx->parser != NULL) { xmlRelaxNGFreeParserCtxt(ctx->parser); xmlCleanupParser(); } if(ctx->valid != NULL) { xmlRelaxNGFreeValidCtxt(ctx->valid); } if (ctx->rng != NULL) { xmlRelaxNGFree(ctx->rng); } crm_free(ctx); } return valid; } void crm_xml_cleanup(void) { int lpc = 0; relaxng_ctx_cache_t *ctx = NULL; crm_info("Cleaning up memory from libxml2"); for(; lpc < all_schemas; lpc++) { switch(known_schemas[lpc].type) { case 0: /* None */ break; case 1: /* DTD - Not cached */ break; case 2: /* RNG - Cached */ ctx = (relaxng_ctx_cache_t *)known_schemas[lpc].cache; if(ctx == NULL) { break; } if(ctx->parser != NULL) { xmlRelaxNGFreeParserCtxt(ctx->parser); } if(ctx->valid != NULL) { xmlRelaxNGFreeValidCtxt(ctx->valid); } if (ctx->rng != NULL) { xmlRelaxNGFree(ctx->rng); } crm_free(ctx); known_schemas[lpc].cache = NULL; break; default: break; } } xmlCleanupParser(); } static gboolean validate_with(xmlNode *xml, int method, gboolean to_logs) { xmlDocPtr doc = NULL; gboolean valid = FALSE; int type = known_schemas[method].type; const char *file = known_schemas[method].location; CRM_CHECK(xml != NULL, return FALSE); doc = getDocPtr(xml); crm_debug_2("Validating with: %s (type=%d)", crm_str(file), type); switch(type) { case 0: valid = TRUE; break; case 1: valid = validate_with_dtd(doc, to_logs, file); break; case 2: valid = validate_with_relaxng(doc, to_logs, file, (relaxng_ctx_cache_t**)&(known_schemas[method].cache)); break; default: crm_err("Unknown validator type: %d", type); break; } return valid; } #include static void dump_file(const char *filename) { FILE *fp = NULL; int ch, line = 0; CRM_CHECK(filename != NULL, return); fp = fopen(filename, "r"); CRM_CHECK(fp != NULL, return); fprintf(stderr, "%4d ", ++line); do { ch = getc(fp); if(ch == EOF) { putc('\n', stderr); break; } else if(ch == '\n') { fprintf(stderr, "\n%4d ", ++line); } else { putc(ch, stderr); } } while(1); fclose(fp); } gboolean validate_xml_verbose(xmlNode *xml_blob) { xmlDoc *doc = NULL; xmlNode *xml = NULL; gboolean rc = FALSE; char *filename = NULL; static char *template = NULL; if(template == NULL) { template = crm_strdup(CRM_STATE_DIR"/cib-invalid.XXXXXX"); } filename = mktemp(template); write_xml_file(xml_blob, filename, FALSE); dump_file(filename); doc = xmlParseFile(filename); xml = xmlDocGetRootElement(doc); rc = validate_xml(xml, NULL, FALSE); free_xml(xml); return rc; } gboolean validate_xml(xmlNode *xml_blob, const char *validation, gboolean to_logs) { int lpc = 0; if(validation == NULL) { validation = crm_element_value(xml_blob, XML_ATTR_VALIDATION); } if(validation == NULL) { validation = crm_element_value(xml_blob, "ignore-dtd"); if(crm_is_true(validation)) { validation = "none"; } else { validation = "transitional-0.6"; } } if(safe_str_eq(validation, "none")) { return TRUE; } for(; lpc < all_schemas; lpc++) { if(safe_str_eq(validation, known_schemas[lpc].name)) { return validate_with(xml_blob, lpc, to_logs); } } crm_err("Unknown validator: %s", validation); return FALSE; } static xmlNode *apply_transformation(xmlNode *xml, const char *transform) { xmlNode *out = NULL; xmlDocPtr res = NULL; xmlDocPtr doc = NULL; xsltStylesheet *xslt = NULL; CRM_CHECK(xml != NULL, return FALSE); doc = getDocPtr(xml); xmlLoadExtDtdDefaultValue = 1; xmlSubstituteEntitiesDefault(1); xslt = xsltParseStylesheetFile((const xmlChar *)transform); CRM_CHECK(xslt != NULL, goto cleanup); res = xsltApplyStylesheet(xslt, doc, NULL); CRM_CHECK(res != NULL, goto cleanup); out = xmlDocGetRootElement(res); cleanup: if(xslt) { xsltFreeStylesheet(xslt); } xsltCleanupGlobals(); xmlCleanupParser(); return out; } const char *get_schema_name(int version) { if(version < 0 || version >= all_schemas) { return "unknown"; } return known_schemas[version].name; } int get_schema_version(const char *name) { int lpc = 0; for(; lpc < all_schemas; lpc++) { if(safe_str_eq(name, known_schemas[lpc].name)) { return lpc; } } return -1; } /* set which validation to use */ #include int update_validation( xmlNode **xml_blob, int *best, gboolean transform, gboolean to_logs) { xmlNode *xml = NULL; char *value = NULL; int lpc = 0, match = -1, rc = cib_ok; CRM_CHECK(best != NULL, return cib_invalid_argument); CRM_CHECK(xml_blob != NULL, return cib_invalid_argument); CRM_CHECK(*xml_blob != NULL, return cib_invalid_argument); *best = 0; xml = *xml_blob; value = crm_element_value_copy(xml, XML_ATTR_VALIDATION); if(value != NULL) { match = get_schema_version(value); lpc = match; if(lpc >= 0 && transform == FALSE) { lpc++; } else if(lpc < 0) { crm_debug("Unknown validation type"); lpc = 0; } } if(match >= max_schemas) { /* nothing to do */ crm_free(value); *best = match; return cib_ok; } for(; lpc < max_schemas; lpc++) { gboolean valid = TRUE; crm_debug("Testing '%s' validation", known_schemas[lpc].name?known_schemas[lpc].name:""); valid = validate_with(xml, lpc, to_logs); if(valid) { *best = lpc; } if(valid && transform) { xmlNode *upgrade = NULL; int next = known_schemas[lpc].after_transform; if(next <= 0) { next = lpc+1; } crm_notice("Upgrading %s-style configuration to %s with %s", known_schemas[lpc].name, known_schemas[next].name, known_schemas[lpc].transform?known_schemas[lpc].transform:"no-op"); if(known_schemas[lpc].transform == NULL) { if(validate_with(xml, next, to_logs)) { crm_debug("Configuration valid for schema: %s", known_schemas[next].name); lpc = next; *best = next; rc = cib_ok; } else { crm_info("Configuration not valid for schema: %s", known_schemas[next].name); } } else { upgrade = apply_transformation(xml, known_schemas[lpc].transform); if(upgrade == NULL) { crm_err("Transformation %s failed", known_schemas[lpc].transform); rc = cib_transform_failed; } else if(validate_with(upgrade, next, to_logs)) { crm_info("Transformation %s successful", known_schemas[lpc].transform); lpc = next; *best = next; free_xml(xml); xml = upgrade; rc = cib_ok; } else { crm_err("Transformation %s did not produce a valid configuration", known_schemas[lpc].transform); crm_log_xml_info(upgrade, "transform:bad"); free_xml(upgrade); rc = cib_dtd_validation; } } } } if(*best > match) { crm_notice("Upgraded from %s to %s validation", value?value:"", known_schemas[*best].name); crm_xml_add(xml, XML_ATTR_VALIDATION, known_schemas[*best].name); } *xml_blob = xml; crm_free(value); return rc; } xmlNode * getXpathResult(xmlXPathObjectPtr xpathObj, int index) { xmlNode *match = NULL; CRM_CHECK(index >= 0, return NULL); CRM_CHECK(xpathObj != NULL, return NULL); if(index >= xpathObj->nodesetval->nodeNr) { crm_err("Requested index %d of only %d items", index, xpathObj->nodesetval->nodeNr); return NULL; } match = xpathObj->nodesetval->nodeTab[index]; CRM_CHECK(match != NULL, return NULL); /* * From xpath2.c * * All the elements returned by an XPath query are pointers to * elements from the tree *except* namespace nodes where the XPath * semantic is different from the implementation in libxml2 tree. * As a result when a returned node set is freed when * xmlXPathFreeObject() is called, that routine must check the * element type. But node from the returned set may have been removed * by xmlNodeSetContent() resulting in access to freed data. * This can be exercised by running * valgrind xpath2 test3.xml '//discarded' discarded * There is 2 ways around it: * - make a copy of the pointers to the nodes from the result set * then call xmlXPathFreeObject() and then modify the nodes * or * - remove the reference to the modified nodes from the node set * as they are processed, if they are not namespace nodes. */ if (xpathObj->nodesetval->nodeTab[index]->type != XML_NAMESPACE_DECL) { xpathObj->nodesetval->nodeTab[index] = NULL; } if(match->type == XML_DOCUMENT_NODE) { /* Will happen if section = '/' */ match = match->children; } else if(match->type != XML_ELEMENT_NODE && match->parent && match->parent->type == XML_ELEMENT_NODE) { /* reurning the parent instead */ match = match->parent; } else if(match->type != XML_ELEMENT_NODE) { /* We only support searching nodes */ crm_err("We only support %d not %d", XML_ELEMENT_NODE, match->type); match = NULL; } return match; } /* the caller needs to check if the result contains a xmlDocPtr or xmlNodePtr */ xmlXPathObjectPtr xpath_search(xmlNode *xml_top, const char *path) { xmlDocPtr doc = NULL; xmlXPathObjectPtr xpathObj = NULL; xmlXPathContextPtr xpathCtx = NULL; const xmlChar *xpathExpr = (const xmlChar *)path; CRM_CHECK(path != NULL, return NULL); CRM_CHECK(xml_top != NULL, return NULL); CRM_CHECK(strlen(path) > 0, return NULL); doc = getDocPtr(xml_top); crm_debug_2("Evaluating: %s", path); xpathCtx = xmlXPathNewContext(doc); CRM_ASSERT(xpathCtx != NULL); xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx); xmlXPathFreeContext(xpathCtx); return xpathObj; } gboolean cli_config_update(xmlNode **xml, int *best_version, gboolean to_logs) { gboolean rc = TRUE; static int min_version = -1; static int max_version = -1; const char *value = crm_element_value(*xml, XML_ATTR_VALIDATION); int version = get_schema_version(value); if(min_version < 0) { min_version = get_schema_version(MINIMUM_SCHEMA_VERSION); } if(max_version < 0) { max_version = get_schema_version(LATEST_SCHEMA_VERSION); } if(version < min_version) { xmlNode *converted = NULL; converted = copy_xml(*xml); update_validation(&converted, &version, TRUE, to_logs); value = crm_element_value(converted, XML_ATTR_VALIDATION); if(version < min_version) { if(to_logs) { crm_config_err("Your current configuration could only be upgraded to %s... " - "the minimum requirement is %s.\n", crm_str(value), MINIMUM_SCHEMA_VERSION); + "the minimum requirement is %s.\n", crm_str(value), MINIMUM_SCHEMA_VERSION); } else { fprintf(stderr, "Your current configuration could only be upgraded to %s... " "the minimum requirement is %s.\n", crm_str(value), MINIMUM_SCHEMA_VERSION); } free_xml(converted); converted = NULL; rc = FALSE; } else { free_xml(*xml); *xml = converted; if(version < max_version) { crm_config_warn("Your configuration was internally updated to %s... " "which is acceptable but not the most recent", get_schema_name(version)); } else if(to_logs){ crm_info("Your configuration was internally updated to the latest version (%s)", - get_schema_name(version)); + get_schema_name(version)); } } } else if(version > max_version) { if(to_logs){ crm_config_warn("Configuration validation is currently disabled." " It is highly encouraged and prevents many common cluster issues."); } else { fprintf(stderr, "Configuration validation is currently disabled." - " It is highly encouraged and prevents many common cluster issues.\n"); + " It is highly encouraged and prevents many common cluster issues.\n"); } } if(best_version) { *best_version = version; } return rc; } xmlNode *expand_idref(xmlNode *input, xmlNode *top) { const char *tag = NULL; const char *ref = NULL; xmlNode *result = input; char *xpath_string = NULL; if(result == NULL) { return NULL; } else if(top == NULL) { top = input; } tag = crm_element_name(result); ref = crm_element_value(result, XML_ATTR_IDREF); if(ref != NULL) { int xpath_max = 512, offset = 0; crm_malloc0(xpath_string, xpath_max); offset += snprintf(xpath_string + offset, xpath_max - offset, "//%s[@id='%s']", tag, ref); result = get_xpath_object(xpath_string, top, LOG_ERR); if(result == NULL) { char *nodePath = (char *)xmlGetNodePath(top); crm_err("No match for %s found in %s: Invalid configuration", xpath_string, crm_str(nodePath)); crm_free(nodePath); } } crm_free(xpath_string); return result; } xmlNode* get_xpath_object_relative(const char *xpath, xmlNode *xml_obj, int error_level) { int len = 0; xmlNode *result = NULL; char *xpath_full = NULL; char *xpath_prefix = NULL; if(xml_obj == NULL || xpath == NULL) { return NULL; } xpath_prefix = (char *)xmlGetNodePath(xml_obj); len += strlen(xpath_prefix); len += strlen(xpath); xpath_full = crm_strdup(xpath_prefix); crm_realloc(xpath_full, len+1); strncat(xpath_full, xpath, len); result = get_xpath_object(xpath_full, xml_obj, error_level); crm_free(xpath_prefix); crm_free(xpath_full); return result; } xmlNode* get_xpath_object(const char *xpath, xmlNode *xml_obj, int error_level) { xmlNode *result = NULL; xmlXPathObjectPtr xpathObj = NULL; char *nodePath = NULL; char *matchNodePath = NULL; if(xpath == NULL) { return xml_obj; /* or return NULL? */ } xpathObj = xpath_search(xml_obj, xpath); nodePath = (char *)xmlGetNodePath(xml_obj); if(xpathObj == NULL || xpathObj->nodesetval == NULL || xpathObj->nodesetval->nodeNr < 1) { do_crm_log(error_level, "No match for %s in %s", xpath, crm_str(nodePath)); crm_log_xml(error_level+1, "Unexpected Input", xml_obj); } else if(xpathObj->nodesetval->nodeNr > 1) { int lpc = 0, max = xpathObj->nodesetval->nodeNr; do_crm_log(error_level, "Too many matches for %s in %s", xpath, crm_str(nodePath)); for(lpc = 0; lpc < max; lpc++) { xmlNode *match = getXpathResult(xpathObj, lpc); CRM_CHECK(match != NULL, continue); matchNodePath = (char *)xmlGetNodePath(match); do_crm_log(error_level, "%s[%d] = %s", xpath, lpc, crm_str(matchNodePath)); crm_free(matchNodePath); } crm_log_xml(LOG_DEBUG_2, "Bad Input", xml_obj); } else { result = getXpathResult(xpathObj, 0); } if(xpathObj) { xmlXPathFreeObject(xpathObj); } crm_free(nodePath); return result; } const char * crm_element_value(xmlNode *data, const char *name) { xmlAttr *attr = NULL; if(data == NULL) { crm_err("Couldn't find %s in NULL", name?name:""); return NULL; } else if(name == NULL) { crm_err("Couldn't find NULL in %s", crm_element_name(data)); return NULL; } attr = xmlHasProp(data, (const xmlChar*)name); if(attr == NULL || attr->children == NULL) { return NULL; } return (const char*)attr->children->content; } diff --git a/lib/pengine/clone.c b/lib/pengine/clone.c index 663362c707..209c565310 100644 --- a/lib/pengine/clone.c +++ b/lib/pengine/clone.c @@ -1,504 +1,509 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #define VARIANT_CLONE 1 #include "./variant.h" void clone_create_notifications( resource_t *rsc, action_t *action, action_t *action_complete, pe_working_set_t *data_set); void force_non_unique_clone(resource_t *rsc, const char *rid, pe_working_set_t *data_set); resource_t *create_child_clone(resource_t *rsc, int sub_id, pe_working_set_t *data_set); static void mark_as_orphan(resource_t *rsc) { GListPtr gIter = rsc->children; set_bit(rsc->flags, pe_rsc_orphan); for(; gIter != NULL; gIter = gIter->next) { resource_t *child = (resource_t*)gIter->data; mark_as_orphan(child); } } static void clear_bit_recursive(resource_t *rsc, unsigned long long flag) { GListPtr gIter = rsc->children; clear_bit_inplace(rsc->flags, flag); for(; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t*)gIter->data; clear_bit_recursive(child_rsc, flag); } } void force_non_unique_clone(resource_t *rsc, const char *rid, pe_working_set_t *data_set) { if(rsc->variant == pe_clone || rsc->variant == pe_master) { clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); crm_config_warn("Clones %s contains non-OCF resource %s and so " "can only be used as an anonymous clone. " "Set the "XML_RSC_ATTR_UNIQUE" meta attribute to false", rsc->id, rid); clone_data->clone_node_max = 1; clone_data->clone_max = g_list_length(data_set->nodes); clear_bit_recursive(rsc, pe_rsc_unique); } } resource_t * find_clone_instance(resource_t *rsc, const char *sub_id, pe_working_set_t *data_set) { char *child_id = NULL; resource_t *child = NULL; const char *child_base = NULL; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); child_base = ID(clone_data->xml_obj_child); child_id = crm_concat(child_base, sub_id, ':'); child = pe_find_resource(rsc->children, child_id); crm_free(child_id); return child; } resource_t * create_child_clone(resource_t *rsc, int sub_id, pe_working_set_t *data_set) { gboolean as_orphan = FALSE; char *inc_num = NULL; char *inc_max = NULL; resource_t *child_rsc = NULL; xmlNode * child_copy = NULL; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); CRM_CHECK(clone_data->xml_obj_child != NULL, return FALSE); if(sub_id < 0) { as_orphan = TRUE; sub_id = clone_data->total_clones; } inc_num = crm_itoa(sub_id); inc_max = crm_itoa(clone_data->clone_max); child_copy = copy_xml(clone_data->xml_obj_child); crm_xml_add(child_copy, XML_RSC_ATTR_INCARNATION, inc_num); if(common_unpack(child_copy, &child_rsc, rsc, data_set) == FALSE) { pe_err("Failed unpacking resource %s", crm_element_value(child_copy, XML_ATTR_ID)); child_rsc = NULL; goto bail; } /* child_rsc->globally_unique = rsc->globally_unique; */ clone_data->total_clones += 1; crm_debug_2("Setting clone attributes for: %s", child_rsc->id); rsc->children = g_list_append(rsc->children, child_rsc); if(as_orphan) { mark_as_orphan(child_rsc); } add_hash_param(child_rsc->meta, XML_RSC_ATTR_INCARNATION_MAX, inc_max); print_resource(LOG_DEBUG_3, "Added", child_rsc, FALSE); bail: crm_free(inc_num); crm_free(inc_max); return child_rsc; } gboolean master_unpack(resource_t *rsc, pe_working_set_t *data_set) { const char *master_max = g_hash_table_lookup( rsc->meta, XML_RSC_ATTR_MASTER_MAX); const char *master_node_max = g_hash_table_lookup( rsc->meta, XML_RSC_ATTR_MASTER_NODEMAX); g_hash_table_replace(rsc->meta, crm_strdup("stateful"), crm_strdup(XML_BOOLEAN_TRUE)); if(clone_unpack(rsc, data_set)) { clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); clone_data->master_max = crm_parse_int(master_max, "1"); clone_data->master_node_max = crm_parse_int(master_node_max, "1"); return TRUE; } return FALSE; } gboolean clone_unpack(resource_t *rsc, pe_working_set_t *data_set) { int lpc = 0; const char *type = NULL; resource_t *self = NULL; int num_xml_children = 0; + xmlNode *a_child = NULL; xmlNode *xml_tmp = NULL; xmlNode *xml_self = NULL; xmlNode *xml_obj = rsc->xml; clone_variant_data_t *clone_data = NULL; const char *ordered = g_hash_table_lookup( rsc->meta, XML_RSC_ATTR_ORDERED); const char *interleave = g_hash_table_lookup( rsc->meta, XML_RSC_ATTR_INTERLEAVE); const char *max_clones = g_hash_table_lookup( rsc->meta, XML_RSC_ATTR_INCARNATION_MAX); const char *max_clones_node = g_hash_table_lookup( rsc->meta, XML_RSC_ATTR_INCARNATION_NODEMAX); crm_debug_3("Processing resource %s...", rsc->id); crm_malloc0(clone_data, sizeof(clone_variant_data_t)); rsc->variant_opaque = clone_data; clone_data->interleave = FALSE; clone_data->ordered = FALSE; clone_data->active_clones = 0; clone_data->xml_obj_child = NULL; clone_data->clone_node_max = crm_parse_int(max_clones_node, "1"); if(max_clones) { clone_data->clone_max = crm_parse_int(max_clones, "1"); } else if(g_list_length(data_set->nodes) > 0) { clone_data->clone_max = g_list_length(data_set->nodes); } else { clone_data->clone_max = 1; /* Handy during crm_verify */ } if(crm_is_true(interleave)) { clone_data->interleave = TRUE; } if(crm_is_true(ordered)) { clone_data->ordered = TRUE; } if((rsc->flags & pe_rsc_unique) == 0 && clone_data->clone_node_max > 1) { crm_config_err("Anonymous clones (%s) may only support one copy" " per node", rsc->id); clone_data->clone_node_max = 1; } crm_debug_2("Options for %s", rsc->id); crm_debug_2("\tClone max: %d", clone_data->clone_max); crm_debug_2("\tClone node max: %d", clone_data->clone_node_max); crm_debug_2("\tClone is unique: %s", is_set(rsc->flags, pe_rsc_unique)?"true":"false"); clone_data->xml_obj_child = find_xml_node( xml_obj, XML_CIB_TAG_GROUP, FALSE); if(clone_data->xml_obj_child == NULL) { - clone_data->xml_obj_child = find_xml_node( - xml_obj, XML_CIB_TAG_RESOURCE, TRUE); - } else { - xml_child_iter_filter(xml_obj, a_child, XML_CIB_TAG_RESOURCE, num_xml_children++); + clone_data->xml_obj_child = find_xml_node(xml_obj, XML_CIB_TAG_RESOURCE, TRUE); + for(a_child = xml_obj; a_child != NULL; a_child = a_child->next) { + if(crm_str_eq((const char *)a_child->name, XML_CIB_TAG_RESOURCE, TRUE)) { + num_xml_children++; + } + } } if(clone_data->xml_obj_child == NULL) { crm_config_err("%s has nothing to clone", rsc->id); return FALSE; } - type = crm_element_name(clone_data->xml_obj_child); - xml_child_iter_filter(xml_obj, a_child, type, num_xml_children++); + for(a_child = xml_obj; a_child != NULL; a_child = a_child->next) { + if(crm_str_eq((const char *)a_child->name, type, TRUE)) { + num_xml_children++; + } + } if(num_xml_children > 1) { crm_config_err("%s has too many children. Only the first (%s) will be cloned.", rsc->id, ID(clone_data->xml_obj_child)); } xml_self = copy_xml(rsc->xml); /* this is a bit of a hack - but simplifies everything else */ xmlNodeSetName(xml_self, ((const xmlChar*)XML_CIB_TAG_RESOURCE)); xml_tmp = find_xml_node(xml_obj, "operations", FALSE); if(xml_tmp != NULL) { add_node_copy(xml_self, xml_tmp); } /* Make clones ever so slightly sticky by default * This is the only way to ensure clone instances are not * shuffled around the cluster for no benefit */ add_hash_param(rsc->meta, XML_RSC_ATTR_STICKINESS, "1"); if(common_unpack(xml_self, &self, rsc, data_set)) { clone_data->self = self; } else { crm_log_xml_err(xml_self, "Couldnt unpack dummy child"); clone_data->self = self; return FALSE; } crm_debug_2("\tClone is unique (fixed): %s", is_set(rsc->flags, pe_rsc_unique)?"true":"false"); clone_data->notify_confirm = is_set(rsc->flags, pe_rsc_notify); add_hash_param(rsc->meta, XML_RSC_ATTR_UNIQUE, is_set(rsc->flags, pe_rsc_unique)?XML_BOOLEAN_TRUE:XML_BOOLEAN_FALSE); for(lpc = 0; lpc < clone_data->clone_max; lpc++) { create_child_clone(rsc, lpc, data_set); } if(clone_data->clone_max == 0) { /* create one so that unpack_find_resource() will hook up * any orphans up to the parent correctly */ create_child_clone(rsc, -1, data_set); } crm_debug_3("Added %d children to resource %s...", clone_data->clone_max, rsc->id); return TRUE; } gboolean clone_active(resource_t *rsc, gboolean all) { GListPtr gIter = rsc->children; for(; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t*)gIter->data; gboolean child_active = child_rsc->fns->active(child_rsc, all); if(all == FALSE && child_active) { return TRUE; } else if(all && child_active == FALSE) { return FALSE; } } if(all) { return TRUE; } else { return FALSE; } } static char * add_list_element(char *list, const char *value) { int len = 0; int last = 0; if(value == NULL) { return list; } if(list) { last = strlen(list); } len = last + 2; /* +1 space, +1 EOS */ len += strlen(value); crm_realloc(list, len); sprintf(list + last, " %s", value); return list; } static void short_print(char *list, const char *prefix, const char *type, long options, void *print_data) { if(list) { if(options & pe_print_html) { status_print("
  • "); } status_print("%s%s: [%s ]", prefix, type, list); if(options & pe_print_html) { status_print("
  • \n"); } else if(options & pe_print_suppres_nl) { /* nothing */ } else if((options & pe_print_printf) || (options & pe_print_ncurses)) { status_print("\n"); } } } void clone_print( resource_t *rsc, const char *pre_text, long options, void *print_data) { char *child_text = NULL; char *master_list = NULL; char *started_list = NULL; char *stopped_list = NULL; const char *type = "Clone"; GListPtr gIter = rsc->children; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); if(pre_text == NULL) { pre_text = " "; } child_text = crm_concat(pre_text, " ", ' '); if(rsc->variant == pe_master) { type = "Master/Slave"; } status_print("%s%s Set: %s [%s]%s%s", pre_text?pre_text:"", type, rsc->id, ID(clone_data->xml_obj_child), is_set(rsc->flags, pe_rsc_unique)?" (unique)":"", is_set(rsc->flags, pe_rsc_managed)?"":" (unmanaged)"); if(options & pe_print_html) { status_print("\n
      \n"); } else if((options & pe_print_log) == 0) { status_print("\n"); } for(; gIter != NULL; gIter = gIter->next) { gboolean print_full = FALSE; resource_t *child_rsc = (resource_t*)gIter->data; if(child_rsc->fns->active(child_rsc, FALSE) == FALSE) { /* Inactive clone */ if(is_set(child_rsc->flags, pe_rsc_orphan)) { continue; } else if(is_set(rsc->flags, pe_rsc_unique)) { print_full = TRUE; } else { stopped_list = add_list_element(stopped_list, child_rsc->id); } } else if(is_set(child_rsc->flags, pe_rsc_unique) || is_set(child_rsc->flags, pe_rsc_orphan) || is_set(child_rsc->flags, pe_rsc_managed) == FALSE || is_set(child_rsc->flags, pe_rsc_failed)) { /* Unique, unmanaged or failed clone */ print_full = TRUE; } else if(child_rsc->fns->active(child_rsc, TRUE)) { /* Fully active anonymous clone */ node_t *location = child_rsc->fns->location(child_rsc, NULL, TRUE); if(location) { const char *host = location->details->uname; enum rsc_role_e a_role = child_rsc->fns->state(child_rsc, TRUE); if(a_role > RSC_ROLE_SLAVE) { /* And active on a single node as master */ master_list = add_list_element(master_list, host); } else { /* And active on a single node as started/slave */ started_list = add_list_element(started_list, host); } } else { /* uncolocated group - bleh */ print_full = TRUE; } } else { /* Partially active anonymous clone */ print_full = TRUE; } if(print_full) { if(options & pe_print_html) { status_print("
    • \n"); } child_rsc->fns->print( child_rsc, child_text, options, print_data); if(options & pe_print_html) { status_print("
    • \n"); } } } short_print(master_list, child_text, "Masters", options, print_data); short_print(started_list, child_text, rsc->variant==pe_master?"Slaves":"Started", options, print_data); short_print(stopped_list, child_text, "Stopped", options, print_data); crm_free(master_list); crm_free(started_list); crm_free(stopped_list); if(options & pe_print_html) { status_print("
    \n"); } crm_free(child_text); } void clone_free(resource_t *rsc) { GListPtr gIter = rsc->children; clone_variant_data_t *clone_data = NULL; get_clone_variant_data(clone_data, rsc); crm_debug_3("Freeing %s", rsc->id); for(; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t*)gIter->data; crm_debug_3("Freeing child %s", child_rsc->id); free_xml(child_rsc->xml); child_rsc->fns->free(child_rsc); } - crm_debug_3("Freeing child list"); - pe_free_shallow_adv(rsc->children, FALSE); + g_list_free(rsc->children); if(clone_data->self) { free_xml(clone_data->self->xml); clone_data->self->fns->free(clone_data->self); } CRM_ASSERT(clone_data->demote_notify == NULL); CRM_ASSERT(clone_data->stop_notify == NULL); CRM_ASSERT(clone_data->start_notify == NULL); CRM_ASSERT(clone_data->promote_notify == NULL); common_free(rsc); } enum rsc_role_e clone_resource_state(const resource_t *rsc, gboolean current) { enum rsc_role_e clone_role = RSC_ROLE_UNKNOWN; GListPtr gIter = rsc->children; for(; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t*)gIter->data; enum rsc_role_e a_role = child_rsc->fns->state(child_rsc, current); if(a_role > clone_role) { clone_role = a_role; } } crm_debug_3("%s role: %s", rsc->id, role2text(clone_role)); return clone_role; } diff --git a/lib/pengine/complex.c b/lib/pengine/complex.c index bf74eb5a6a..ed51eef32f 100644 --- a/lib/pengine/complex.c +++ b/lib/pengine/complex.c @@ -1,472 +1,472 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include extern xmlNode *get_object_root(const char *object_type,xmlNode *the_root); void populate_hash(xmlNode *nvpair_list, GHashTable *hash, const char **attrs, int attrs_length); resource_object_functions_t resource_class_functions[] = { { native_unpack, native_find_rsc, native_parameter, native_print, native_active, native_resource_state, native_location, native_free }, { group_unpack, native_find_rsc, native_parameter, group_print, group_active, group_resource_state, native_location, group_free }, { clone_unpack, native_find_rsc, native_parameter, clone_print, clone_active, clone_resource_state, native_location, clone_free }, { master_unpack, native_find_rsc, native_parameter, clone_print, clone_active, clone_resource_state, native_location, clone_free } }; enum pe_obj_types get_resource_type(const char *name) { if(safe_str_eq(name, XML_CIB_TAG_RESOURCE)) { return pe_native; } else if(safe_str_eq(name, XML_CIB_TAG_GROUP)) { return pe_group; } else if(safe_str_eq(name, XML_CIB_TAG_INCARNATION)) { return pe_clone; } else if(safe_str_eq(name, XML_CIB_TAG_MASTER)) { return pe_master; } return pe_unknown; } const char *get_resource_typename(enum pe_obj_types type) { switch(type) { case pe_native: return XML_CIB_TAG_RESOURCE; case pe_group: return XML_CIB_TAG_GROUP; case pe_clone: return XML_CIB_TAG_INCARNATION; case pe_master: return XML_CIB_TAG_MASTER; case pe_unknown: return "unknown"; } return ""; } static void dup_attr(gpointer key, gpointer value, gpointer user_data) { add_hash_param(user_data, key, value); } void get_meta_attributes(GHashTable *meta_hash, resource_t *rsc, node_t *node, pe_working_set_t *data_set) { GHashTable *node_hash = NULL; if(node) { node_hash = node->details->attrs; } xml_prop_iter(rsc->xml, prop_name, prop_value, add_hash_param(meta_hash, prop_name, prop_value); ); unpack_instance_attributes(data_set->input, rsc->xml, XML_TAG_META_SETS, node_hash, meta_hash, NULL, FALSE, data_set->now); /* populate from the regular attributes until the GUI can create * meta attributes */ unpack_instance_attributes(data_set->input, rsc->xml, XML_TAG_ATTR_SETS, node_hash, meta_hash, NULL, FALSE, data_set->now); /* set anything else based on the parent */ if(rsc->parent != NULL) { g_hash_table_foreach(rsc->parent->meta, dup_attr, meta_hash); } /* and finally check the defaults */ unpack_instance_attributes(data_set->input, data_set->rsc_defaults, XML_TAG_META_SETS, node_hash, meta_hash, NULL, FALSE, data_set->now); } void get_rsc_attributes(GHashTable *meta_hash, resource_t *rsc, node_t *node, pe_working_set_t *data_set) { GHashTable *node_hash = NULL; if(node) { node_hash = node->details->attrs; } unpack_instance_attributes(data_set->input, rsc->xml, XML_TAG_ATTR_SETS, node_hash, meta_hash, NULL, FALSE, data_set->now); /* set anything else based on the parent */ if(rsc->parent != NULL) { get_rsc_attributes(meta_hash, rsc->parent, node, data_set); } else { /* and finally check the defaults */ unpack_instance_attributes(data_set->input, data_set->rsc_defaults, XML_TAG_ATTR_SETS, node_hash, meta_hash, NULL, FALSE, data_set->now); } } gboolean common_unpack(xmlNode * xml_obj, resource_t **rsc, resource_t *parent, pe_working_set_t *data_set) { xmlNode *ops = NULL; resource_t *top = NULL; const char *value = NULL; const char *id = crm_element_value(xml_obj, XML_ATTR_ID); const char *class = crm_element_value(xml_obj, XML_AGENT_ATTR_CLASS); crm_log_xml_debug_3(xml_obj, "Processing resource input..."); if(id == NULL) { pe_err("Must specify id tag in "); return FALSE; } else if(rsc == NULL) { pe_err("Nowhere to unpack resource into"); return FALSE; } crm_malloc0(*rsc, sizeof(resource_t)); ops = find_xml_node(xml_obj, "operations", FALSE); (*rsc)->xml = xml_obj; (*rsc)->parent = parent; (*rsc)->ops_xml = expand_idref(ops, data_set->input); (*rsc)->variant = get_resource_type(crm_element_name(xml_obj)); if((*rsc)->variant == pe_unknown) { pe_err("Unknown resource type: %s", crm_element_name(xml_obj)); crm_free(*rsc); return FALSE; } (*rsc)->parameters = g_hash_table_new_full( g_str_hash,g_str_equal, g_hash_destroy_str,g_hash_destroy_str); (*rsc)->meta = g_hash_table_new_full( g_str_hash,g_str_equal, g_hash_destroy_str,g_hash_destroy_str); (*rsc)->allowed_nodes = g_hash_table_new_full( g_str_hash,g_str_equal, NULL, g_hash_destroy_str); (*rsc)->known_on = g_hash_table_new_full( g_str_hash,g_str_equal, NULL, g_hash_destroy_str); value = crm_element_value(xml_obj, XML_RSC_ATTR_INCARNATION); if(value) { (*rsc)->id = crm_concat(id, value, ':'); add_hash_param((*rsc)->meta, XML_RSC_ATTR_INCARNATION, value); } else { (*rsc)->id = crm_strdup(id); } if(parent) { (*rsc)->long_name = crm_concat(parent->long_name, (*rsc)->id, ':'); } else { (*rsc)->long_name = crm_strdup((*rsc)->id); } (*rsc)->fns = &resource_class_functions[(*rsc)->variant]; crm_debug_3("Unpacking resource..."); get_meta_attributes((*rsc)->meta, *rsc, NULL, data_set); (*rsc)->flags = 0; set_bit((*rsc)->flags, pe_rsc_runnable); set_bit((*rsc)->flags, pe_rsc_provisional); if(is_set(data_set->flags, pe_flag_is_managed_default)) { set_bit((*rsc)->flags, pe_rsc_managed); } (*rsc)->rsc_cons = NULL; (*rsc)->actions = NULL; (*rsc)->role = RSC_ROLE_STOPPED; (*rsc)->next_role = RSC_ROLE_UNKNOWN; (*rsc)->recovery_type = recovery_stop_start; (*rsc)->stickiness = data_set->default_resource_stickiness; (*rsc)->migration_threshold= INFINITY; (*rsc)->failure_timeout = 0; value = g_hash_table_lookup((*rsc)->meta, XML_CIB_ATTR_PRIORITY); (*rsc)->priority = crm_parse_int(value, "0"); (*rsc)->effective_priority = (*rsc)->priority; value = g_hash_table_lookup((*rsc)->meta, XML_RSC_ATTR_NOTIFY); if(crm_is_true(value)) { set_bit((*rsc)->flags, pe_rsc_notify); } value = g_hash_table_lookup((*rsc)->meta, XML_RSC_ATTR_MANAGED); if(value != NULL && safe_str_neq("default", value)) { gboolean bool_value = TRUE; crm_str_to_boolean(value, &bool_value); if(bool_value == FALSE) { clear_bit((*rsc)->flags, pe_rsc_managed); } else { set_bit((*rsc)->flags, pe_rsc_managed); } } if(is_set(data_set->flags, pe_flag_maintenance_mode)) { clear_bit((*rsc)->flags, pe_rsc_managed); } crm_debug_2("Options for %s", (*rsc)->id); value = g_hash_table_lookup((*rsc)->meta, XML_RSC_ATTR_UNIQUE); top = uber_parent(*rsc); if(crm_is_true(value) || top->variant < pe_clone) { set_bit((*rsc)->flags, pe_rsc_unique); } value = g_hash_table_lookup((*rsc)->meta, XML_RSC_ATTR_RESTART); if(safe_str_eq(value, "restart")) { (*rsc)->restart_type = pe_restart_restart; crm_debug_2("\tDependency restart handling: restart"); } else { (*rsc)->restart_type = pe_restart_ignore; crm_debug_2("\tDependency restart handling: ignore"); } value = g_hash_table_lookup((*rsc)->meta, XML_RSC_ATTR_MULTIPLE); if(safe_str_eq(value, "stop_only")) { (*rsc)->recovery_type = recovery_stop_only; crm_debug_2("\tMultiple running resource recovery: stop only"); } else if(safe_str_eq(value, "block")) { (*rsc)->recovery_type = recovery_block; crm_debug_2("\tMultiple running resource recovery: block"); } else { (*rsc)->recovery_type = recovery_stop_start; crm_debug_2("\tMultiple running resource recovery: stop/start"); } value = g_hash_table_lookup((*rsc)->meta, XML_RSC_ATTR_STICKINESS); if(value != NULL && safe_str_neq("default", value)) { (*rsc)->stickiness = char2score(value); } value = g_hash_table_lookup((*rsc)->meta, XML_RSC_ATTR_FAIL_STICKINESS); if(value != NULL && safe_str_neq("default", value)) { (*rsc)->migration_threshold = char2score(value); } else if(value == NULL) { /* Make a best-effort guess at a migration threshold for people with 0.6 configs * try with underscores and hyphens, from both the resource and global defaults section */ value = g_hash_table_lookup((*rsc)->meta, "resource-failure-stickiness"); if(value == NULL) { value = g_hash_table_lookup((*rsc)->meta, "resource_failure_stickiness"); } if(value == NULL) { value = g_hash_table_lookup(data_set->config_hash, "default-resource-failure-stickiness"); } if(value == NULL) { value = g_hash_table_lookup(data_set->config_hash, "default_resource_failure_stickiness"); } if(value) { int fail_sticky = char2score(value); if(fail_sticky == -INFINITY) { (*rsc)->migration_threshold = 1; crm_info("Set a migration threshold of %d for %s based on a failure-stickiness of %s", (*rsc)->migration_threshold, (*rsc)->id, value); } else if((*rsc)->stickiness != 0 && fail_sticky != 0) { (*rsc)->migration_threshold = (*rsc)->stickiness / fail_sticky; if((*rsc)->migration_threshold < 0) { /* Make sure it's positive */ (*rsc)->migration_threshold = 0 - (*rsc)->migration_threshold; } (*rsc)->migration_threshold += 1; crm_info("Calculated a migration threshold for %s of %d based on a stickiness of %d/%s", (*rsc)->id, (*rsc)->migration_threshold, (*rsc)->stickiness, value); } } } value = g_hash_table_lookup((*rsc)->meta, XML_RSC_ATTR_FAIL_TIMEOUT); if(value != NULL) { /* call crm_get_msec() and convert back to seconds */ (*rsc)->failure_timeout = (crm_get_msec(value) / 1000); } get_target_role(*rsc, &((*rsc)->next_role)); crm_debug_2("\tDesired next state: %s", (*rsc)->next_role!=RSC_ROLE_UNKNOWN?role2text((*rsc)->next_role):"default"); if((*rsc)->fns->unpack(*rsc, data_set) == FALSE) { return FALSE; } if(is_set(data_set->flags, pe_flag_symmetric_cluster)) { resource_location(*rsc, NULL, 0, "symmetric_default", data_set); } crm_debug_2("\tAction notification: %s", is_set((*rsc)->flags, pe_rsc_notify)?"required":"not required"); if(safe_str_eq(class, "stonith")) { set_bit_inplace(data_set->flags, pe_flag_have_stonith_resource); } (*rsc)->utilization = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); unpack_instance_attributes(data_set->input, (*rsc)->xml, XML_TAG_UTILIZATION, NULL, (*rsc)->utilization, NULL, FALSE, data_set->now); /* data_set->resources = g_list_append(data_set->resources, (*rsc)); */ return TRUE; } void common_update_score(resource_t *rsc, const char *id, int score) { node_t *node = NULL; node = pe_hash_table_lookup(rsc->allowed_nodes, id); if(node != NULL) { crm_debug_2("Updating score for %s on %s: %d + %d", rsc->id, id, node->weight, score); node->weight = merge_weights(node->weight, score); } if(rsc->children) { GListPtr gIter = rsc->children; for(; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t*)gIter->data; common_update_score(child_rsc, id, score); } } } resource_t *uber_parent(resource_t *rsc) { resource_t *parent = rsc; if(parent == NULL) { return NULL; } while(parent->parent != NULL) { parent = parent->parent; } return parent; } void common_free(resource_t *rsc) { if(rsc == NULL) { return; } crm_debug_5("Freeing %s %d", rsc->id, rsc->variant); g_list_free(rsc->rsc_cons); g_list_free(rsc->rsc_cons_lhs); g_list_free(rsc->dangling_migrations); if(rsc->parameters != NULL) { g_hash_table_destroy(rsc->parameters); } if(rsc->meta != NULL) { g_hash_table_destroy(rsc->meta); } if(rsc->utilization != NULL) { g_hash_table_destroy(rsc->utilization); } if(rsc->parent == NULL && is_set(rsc->flags, pe_rsc_orphan)) { free_xml(rsc->xml); } if(rsc->running_on) { g_list_free(rsc->running_on); rsc->running_on = NULL; } if(rsc->known_on) { g_hash_table_destroy(rsc->known_on); rsc->known_on = NULL; } if(rsc->actions) { g_list_free(rsc->actions); rsc->actions = NULL; } if(rsc->allowed_nodes) { g_hash_table_destroy(rsc->allowed_nodes); rsc->allowed_nodes = NULL; } - pe_free_shallow_adv(rsc->rsc_location, FALSE); + g_list_free(rsc->rsc_location); crm_free(rsc->id); crm_free(rsc->long_name); crm_free(rsc->clone_name); crm_free(rsc->allocated_to); crm_free(rsc->variant_opaque); crm_free(rsc); crm_debug_5("Resource freed"); } diff --git a/lib/pengine/group.c b/lib/pengine/group.c index 31363ec8dc..8b0748163e 100644 --- a/lib/pengine/group.c +++ b/lib/pengine/group.c @@ -1,223 +1,225 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #define VARIANT_GROUP 1 #include "./variant.h" gboolean group_unpack(resource_t *rsc, pe_working_set_t *data_set) { - resource_t *self = NULL; - xmlNode *xml_obj = rsc->xml; - xmlNode *xml_self = copy_xml(rsc->xml); - group_variant_data_t *group_data = NULL; - const char *group_ordered = g_hash_table_lookup( - rsc->meta, XML_RSC_ATTR_ORDERED); - const char *group_colocated = g_hash_table_lookup( - rsc->meta, "collocated"); - const char *clone_id = NULL; + resource_t *self = NULL; + xmlNode *xml_obj = rsc->xml; + xmlNode *xml_self = copy_xml(rsc->xml); + xmlNode *xml_native_rsc = NULL; + group_variant_data_t *group_data = NULL; + const char *group_ordered = g_hash_table_lookup( + rsc->meta, XML_RSC_ATTR_ORDERED); + const char *group_colocated = g_hash_table_lookup( + rsc->meta, "collocated"); + const char *clone_id = NULL; - crm_debug_3("Processing resource %s...", rsc->id); + crm_debug_3("Processing resource %s...", rsc->id); - crm_malloc0(group_data, sizeof(group_variant_data_t)); - group_data->num_children = 0; - group_data->self = NULL; - group_data->first_child = NULL; - group_data->last_child = NULL; - rsc->variant_opaque = group_data; + crm_malloc0(group_data, sizeof(group_variant_data_t)); + group_data->num_children = 0; + group_data->self = NULL; + group_data->first_child = NULL; + group_data->last_child = NULL; + rsc->variant_opaque = group_data; - group_data->ordered = TRUE; - group_data->colocated = TRUE; + group_data->ordered = TRUE; + group_data->colocated = TRUE; - if(group_ordered != NULL) { - crm_str_to_boolean(group_ordered, &(group_data->ordered)); - } - if(group_colocated != NULL) { - crm_str_to_boolean(group_colocated, &(group_data->colocated)); - } + if(group_ordered != NULL) { + crm_str_to_boolean(group_ordered, &(group_data->ordered)); + } + if(group_colocated != NULL) { + crm_str_to_boolean(group_colocated, &(group_data->colocated)); + } - /* this is a bit of a hack - but simplifies everything else */ - xmlNodeSetName(xml_self, ((const xmlChar*)XML_CIB_TAG_RESOURCE)); - if(common_unpack(xml_self, &self, NULL, data_set)) { - group_data->self = self; - self->restart_type = pe_restart_restart; - - } else { - crm_log_xml_err(xml_self, "Couldnt unpack dummy child"); - return FALSE; - } + /* this is a bit of a hack - but simplifies everything else */ + xmlNodeSetName(xml_self, ((const xmlChar*)XML_CIB_TAG_RESOURCE)); + if(common_unpack(xml_self, &self, NULL, data_set)) { + group_data->self = self; + self->restart_type = pe_restart_restart; + + } else { + crm_log_xml_err(xml_self, "Couldnt unpack dummy child"); + return FALSE; + } - clone_id = crm_element_value(rsc->xml, XML_RSC_ATTR_INCARNATION); + clone_id = crm_element_value(rsc->xml, XML_RSC_ATTR_INCARNATION); - xml_child_iter_filter( - xml_obj, xml_native_rsc, XML_CIB_TAG_RESOURCE, - - resource_t *new_rsc = NULL; - crm_xml_add(xml_native_rsc, XML_RSC_ATTR_INCARNATION, clone_id); - if(common_unpack(xml_native_rsc, &new_rsc, - rsc, data_set) == FALSE) { - pe_err("Failed unpacking resource %s", - crm_element_value(xml_obj, XML_ATTR_ID)); - if(new_rsc != NULL && new_rsc->fns != NULL) { - new_rsc->fns->free(new_rsc); - } + for(xml_native_rsc = xml_obj; xml_native_rsc != NULL; xml_native_rsc = xml_native_rsc->next) { + if(crm_str_eq((const char *)xml_native_rsc->name, XML_CIB_TAG_RESOURCE, TRUE)) { + resource_t *new_rsc = NULL; + crm_xml_add(xml_native_rsc, XML_RSC_ATTR_INCARNATION, clone_id); + if(common_unpack(xml_native_rsc, &new_rsc, + rsc, data_set) == FALSE) { + pe_err("Failed unpacking resource %s", + crm_element_value(xml_obj, XML_ATTR_ID)); + if(new_rsc != NULL && new_rsc->fns != NULL) { + new_rsc->fns->free(new_rsc); } + } - group_data->num_children++; - rsc->children = g_list_append(rsc->children, new_rsc); + group_data->num_children++; + rsc->children = g_list_append(rsc->children, new_rsc); - if(group_data->first_child == NULL) { - group_data->first_child = new_rsc; - } - group_data->last_child = new_rsc; - print_resource(LOG_DEBUG_3, "Added", new_rsc, FALSE); - ); + if(group_data->first_child == NULL) { + group_data->first_child = new_rsc; + } + group_data->last_child = new_rsc; + print_resource(LOG_DEBUG_3, "Added", new_rsc, FALSE); + } + } + - if(group_data->num_children == 0) { + if(group_data->num_children == 0) { #if 0 - /* Bug #1287 */ - crm_config_err("Group %s did not have any children", rsc->id); - return FALSE; + /* Bug #1287 */ + crm_config_err("Group %s did not have any children", rsc->id); + return FALSE; #else - crm_config_warn("Group %s did not have any children", rsc->id); - return TRUE; + crm_config_warn("Group %s did not have any children", rsc->id); + return TRUE; #endif - } + } - crm_debug_3("Added %d children to resource %s...", - group_data->num_children, rsc->id); + crm_debug_3("Added %d children to resource %s...", + group_data->num_children, rsc->id); - return TRUE; + return TRUE; } gboolean group_active(resource_t *rsc, gboolean all) { gboolean c_all = TRUE; gboolean c_any = FALSE; GListPtr gIter = rsc->children; for(; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t*)gIter->data; if(child_rsc->fns->active(child_rsc, all)) { c_any = TRUE; } else { c_all = FALSE; } } if(c_any == FALSE) { return FALSE; } else if(all && c_all == FALSE) { return FALSE; } return TRUE; } void group_print( resource_t *rsc, const char *pre_text, long options, void *print_data) { char *child_text = NULL; GListPtr gIter = rsc->children; if(pre_text == NULL) { pre_text = " "; } child_text = crm_concat(pre_text, " ", ' '); status_print("%sResource Group: %s", pre_text?pre_text:"", rsc->id); if(options & pe_print_html) { status_print("\n
      \n"); } else if((options & pe_print_log) == 0) { status_print("\n"); } for(; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t*)gIter->data; if(options & pe_print_html) { status_print("
    • \n"); } child_rsc->fns->print( child_rsc, child_text, options, print_data); if(options & pe_print_html) { status_print("
    • \n"); } } if(options & pe_print_html) { status_print("
    \n"); } crm_free(child_text); } void group_free(resource_t *rsc) { GListPtr gIter = rsc->children; group_variant_data_t *group_data = NULL; CRM_CHECK(rsc != NULL, return); get_group_variant_data(group_data, rsc); crm_debug_3("Freeing %s", rsc->id); for(; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t*)gIter->data; crm_debug_3("Freeing child %s", child_rsc->id); child_rsc->fns->free(child_rsc); } crm_debug_3("Freeing child list"); - pe_free_shallow_adv(rsc->children, FALSE); + g_list_free(rsc->children); if(group_data->self != NULL) { free_xml(group_data->self->xml); group_data->self->fns->free(group_data->self); } common_free(rsc); } enum rsc_role_e group_resource_state(const resource_t *rsc, gboolean current) { enum rsc_role_e group_role = RSC_ROLE_UNKNOWN; GListPtr gIter = rsc->children; for(; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t*)gIter->data; enum rsc_role_e role = child_rsc->fns->state(child_rsc, current); if(role > group_role) { group_role = role; } } crm_debug_3("%s role: %s", rsc->id, role2text(group_role)); return group_role; } diff --git a/lib/pengine/rules.c b/lib/pengine/rules.c index 2d5c0f51dc..e096286961 100644 --- a/lib/pengine/rules.c +++ b/lib/pengine/rules.c @@ -1,660 +1,661 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include CRM_TRACE_INIT_DATA(pe_rules); ha_time_t *parse_xml_duration(ha_time_t *start, xmlNode *duration_spec); gboolean test_date_expression(xmlNode *time_expr, ha_time_t *now); gboolean cron_range_satisfied(ha_time_t *now, xmlNode *cron_spec); gboolean test_attr_expression( - xmlNode *expr, GHashTable *hash, ha_time_t *now); + xmlNode *expr, GHashTable *hash, ha_time_t *now); gboolean test_role_expression( - xmlNode *expr, enum rsc_role_e role, ha_time_t *now); + xmlNode *expr, enum rsc_role_e role, ha_time_t *now); gboolean test_ruleset(xmlNode *ruleset, GHashTable *node_hash, ha_time_t *now) { - gboolean ruleset_default = TRUE; - xml_child_iter_filter( - ruleset, rule, XML_TAG_RULE, - - ruleset_default = FALSE; - if(test_rule(rule, node_hash, RSC_ROLE_UNKNOWN, now)) { - return TRUE; - } - ); + gboolean ruleset_default = TRUE; + xmlNode *rule = NULL; + for(rule = ruleset; rule != NULL; rule = rule->next) { + if(crm_str_eq((const char *)rule->name, XML_TAG_RULE, TRUE)) { + ruleset_default = FALSE; + if(test_rule(rule, node_hash, RSC_ROLE_UNKNOWN, now)) { + return TRUE; + } + } + } - return ruleset_default; + return ruleset_default; } gboolean test_rule(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role, ha_time_t *now) { - gboolean test = TRUE; - gboolean empty = TRUE; - gboolean passed = TRUE; - gboolean do_and = TRUE; - const char *value = NULL; - - rule = expand_idref(rule, NULL); - value = crm_element_value(rule, XML_RULE_ATTR_BOOLEAN_OP); - if(safe_str_eq(value, "or")) { - do_and = FALSE; - passed = FALSE; - } - - crm_debug_2("Testing rule %s", ID(rule)); - xml_child_iter( - rule, expr, - test = test_expression(expr, node_hash, role, now); - empty = FALSE; + xmlNode *expr = NULL; + gboolean test = TRUE; + gboolean empty = TRUE; + gboolean passed = TRUE; + gboolean do_and = TRUE; + const char *value = NULL; + + rule = expand_idref(rule, NULL); + value = crm_element_value(rule, XML_RULE_ATTR_BOOLEAN_OP); + if(safe_str_eq(value, "or")) { + do_and = FALSE; + passed = FALSE; + } + + crm_debug_2("Testing rule %s", ID(rule)); + for(expr = rule; expr != NULL; expr = expr->next) { + test = test_expression(expr, node_hash, role, now); + empty = FALSE; - if(test && do_and == FALSE) { - crm_debug_3("Expression %s/%s passed", - ID(rule), ID(expr)); - return TRUE; + if(test && do_and == FALSE) { + crm_debug_3("Expression %s/%s passed", + ID(rule), ID(expr)); + return TRUE; - } else if(test == FALSE && do_and) { - crm_debug_3("Expression %s/%s failed", - ID(rule), ID(expr)); - return FALSE; - } - ); - - if(empty) { - crm_err("Invalid Rule %s: rules must contain at least one expression", ID(rule)); + } else if(test == FALSE && do_and) { + crm_debug_3("Expression %s/%s failed", + ID(rule), ID(expr)); + return FALSE; } + } + + if(empty) { + crm_err("Invalid Rule %s: rules must contain at least one expression", ID(rule)); + } - crm_debug_2("Rule %s %s", ID(rule), passed?"passed":"failed"); - return passed; + crm_debug_2("Rule %s %s", ID(rule), passed?"passed":"failed"); + return passed; } gboolean test_expression(xmlNode *expr, GHashTable *node_hash, enum rsc_role_e role, ha_time_t *now) { - gboolean accept = FALSE; - const char *uname = NULL; + gboolean accept = FALSE; + const char *uname = NULL; - switch(find_expression_type(expr)) { - case nested_rule: - accept = test_rule(expr, node_hash, role, now); - break; - case attr_expr: - case loc_expr: - /* these expressions can never succeed if there is - * no node to compare with - */ - if(node_hash != NULL) { - accept = test_attr_expression(expr, node_hash, now); - } - break; - - case time_expr: - accept = test_date_expression(expr, now); - break; - - case role_expr: - accept = test_role_expression(expr, role, now); - break; - - default: - CRM_CHECK(FALSE /* bad type */, return FALSE); - accept = FALSE; - } - if(node_hash) { - uname = g_hash_table_lookup(node_hash, "#uname"); - } + switch(find_expression_type(expr)) { + case nested_rule: + accept = test_rule(expr, node_hash, role, now); + break; + case attr_expr: + case loc_expr: + /* these expressions can never succeed if there is + * no node to compare with + */ + if(node_hash != NULL) { + accept = test_attr_expression(expr, node_hash, now); + } + break; + + case time_expr: + accept = test_date_expression(expr, now); + break; + + case role_expr: + accept = test_role_expression(expr, role, now); + break; + + default: + CRM_CHECK(FALSE /* bad type */, return FALSE); + accept = FALSE; + } + if(node_hash) { + uname = g_hash_table_lookup(node_hash, "#uname"); + } - crm_debug_2("Expression %s %s on %s", - ID(expr), accept?"passed":"failed", - uname?uname:"all ndoes"); - return accept; + crm_debug_2("Expression %s %s on %s", + ID(expr), accept?"passed":"failed", + uname?uname:"all ndoes"); + return accept; } enum expression_type find_expression_type(xmlNode *expr) { - const char *tag = NULL; - const char *attr = NULL; - attr = crm_element_value(expr, XML_EXPR_ATTR_ATTRIBUTE); - tag = crm_element_name(expr); + const char *tag = NULL; + const char *attr = NULL; + attr = crm_element_value(expr, XML_EXPR_ATTR_ATTRIBUTE); + tag = crm_element_name(expr); - if(safe_str_eq(tag, "date_expression")) { - return time_expr; + if(safe_str_eq(tag, "date_expression")) { + return time_expr; - } else if(safe_str_eq(tag, XML_TAG_RULE)) { - return nested_rule; + } else if(safe_str_eq(tag, XML_TAG_RULE)) { + return nested_rule; - } else if(safe_str_neq(tag, "expression")) { - return not_expr; + } else if(safe_str_neq(tag, "expression")) { + return not_expr; - } else if(safe_str_eq(attr, "#uname") || safe_str_eq(attr, "#id")) { - return loc_expr; + } else if(safe_str_eq(attr, "#uname") || safe_str_eq(attr, "#id")) { + return loc_expr; - } else if(safe_str_eq(attr, "#role")) { - return role_expr; - } + } else if(safe_str_eq(attr, "#role")) { + return role_expr; + } - return attr_expr; + return attr_expr; } gboolean test_role_expression( - xmlNode *expr, enum rsc_role_e role, ha_time_t *now) + xmlNode *expr, enum rsc_role_e role, ha_time_t *now) { - gboolean accept = FALSE; - const char *op = NULL; - const char *value = NULL; + gboolean accept = FALSE; + const char *op = NULL; + const char *value = NULL; - if(role == RSC_ROLE_UNKNOWN) { - return accept; - } + if(role == RSC_ROLE_UNKNOWN) { + return accept; + } - value = crm_element_value(expr, XML_EXPR_ATTR_VALUE); - op = crm_element_value(expr, XML_EXPR_ATTR_OPERATION); + value = crm_element_value(expr, XML_EXPR_ATTR_VALUE); + op = crm_element_value(expr, XML_EXPR_ATTR_OPERATION); - if(safe_str_eq(op, "defined")) { - if(role > RSC_ROLE_STARTED) { - accept = TRUE; - } + if(safe_str_eq(op, "defined")) { + if(role > RSC_ROLE_STARTED) { + accept = TRUE; + } - } else if(safe_str_eq(op, "not_defined")) { - if(role < RSC_ROLE_SLAVE && role > RSC_ROLE_UNKNOWN) { - accept = TRUE; - } + } else if(safe_str_eq(op, "not_defined")) { + if(role < RSC_ROLE_SLAVE && role > RSC_ROLE_UNKNOWN) { + accept = TRUE; + } - } else if(safe_str_eq(op, "eq")) { - if(text2role(value) == role) { - accept = TRUE; - } + } else if(safe_str_eq(op, "eq")) { + if(text2role(value) == role) { + accept = TRUE; + } - } else if(safe_str_eq(op, "ne")) { - /* we will only test "ne" wtih master/slave roles style */ - if(role < RSC_ROLE_SLAVE && role > RSC_ROLE_UNKNOWN) { - accept = FALSE; + } else if(safe_str_eq(op, "ne")) { + /* we will only test "ne" wtih master/slave roles style */ + if(role < RSC_ROLE_SLAVE && role > RSC_ROLE_UNKNOWN) { + accept = FALSE; - } else if(text2role(value) != role) { - accept = TRUE; - } - } - return accept; + } else if(text2role(value) != role) { + accept = TRUE; + } + } + return accept; } gboolean test_attr_expression(xmlNode *expr, GHashTable *hash, ha_time_t *now) { - gboolean accept = FALSE; - int cmp = 0; - const char *h_val = NULL; - - const char *op = NULL; - const char *type = NULL; - const char *attr = NULL; - const char *value = NULL; + gboolean accept = FALSE; + int cmp = 0; + const char *h_val = NULL; + + const char *op = NULL; + const char *type = NULL; + const char *attr = NULL; + const char *value = NULL; - attr = crm_element_value(expr, XML_EXPR_ATTR_ATTRIBUTE); - op = crm_element_value(expr, XML_EXPR_ATTR_OPERATION); - value = crm_element_value(expr, XML_EXPR_ATTR_VALUE); - type = crm_element_value(expr, XML_EXPR_ATTR_TYPE); + attr = crm_element_value(expr, XML_EXPR_ATTR_ATTRIBUTE); + op = crm_element_value(expr, XML_EXPR_ATTR_OPERATION); + value = crm_element_value(expr, XML_EXPR_ATTR_VALUE); + type = crm_element_value(expr, XML_EXPR_ATTR_TYPE); - if(attr == NULL || op == NULL) { - pe_err("Invlaid attribute or operation in expression" - " (\'%s\' \'%s\' \'%s\')", - crm_str(attr), crm_str(op), crm_str(value)); - return FALSE; - } - - if(hash != NULL) { - h_val = (const char*)g_hash_table_lookup(hash, attr); - } + if(attr == NULL || op == NULL) { + pe_err("Invlaid attribute or operation in expression" + " (\'%s\' \'%s\' \'%s\')", + crm_str(attr), crm_str(op), crm_str(value)); + return FALSE; + } + + if(hash != NULL) { + h_val = (const char*)g_hash_table_lookup(hash, attr); + } - if(value != NULL && h_val != NULL) { - if(type == NULL) { - if(safe_str_eq(op, "lt") - || safe_str_eq(op, "lte") - || safe_str_eq(op, "gt") - || safe_str_eq(op, "gte")) { - type = "number"; - - } else { - type = "string"; - } - crm_debug_2("Defaulting to %s based comparison for '%s' op", type, op); - } + if(value != NULL && h_val != NULL) { + if(type == NULL) { + if(safe_str_eq(op, "lt") + || safe_str_eq(op, "lte") + || safe_str_eq(op, "gt") + || safe_str_eq(op, "gte")) { + type = "number"; + + } else { + type = "string"; + } + crm_debug_2("Defaulting to %s based comparison for '%s' op", type, op); + } - if(safe_str_eq(type, "string")) { - cmp = strcasecmp(h_val, value); + if(safe_str_eq(type, "string")) { + cmp = strcasecmp(h_val, value); - } else if(safe_str_eq(type, "number")) { - int h_val_f = crm_parse_int(h_val, NULL); - int value_f = crm_parse_int(value, NULL); + } else if(safe_str_eq(type, "number")) { + int h_val_f = crm_parse_int(h_val, NULL); + int value_f = crm_parse_int(value, NULL); - if(h_val_f < value_f) { - cmp = -1; - } else if(h_val_f > value_f) { - cmp = 1; - } else { - cmp = 0; - } + if(h_val_f < value_f) { + cmp = -1; + } else if(h_val_f > value_f) { + cmp = 1; + } else { + cmp = 0; + } - } else if(safe_str_eq(type, "version")) { - cmp = compare_version(h_val, value); + } else if(safe_str_eq(type, "version")) { + cmp = compare_version(h_val, value); - } - - } else if(value == NULL && h_val == NULL) { - cmp = 0; - } else if(value == NULL) { - cmp = 1; - } else { - cmp = -1; } + + } else if(value == NULL && h_val == NULL) { + cmp = 0; + } else if(value == NULL) { + cmp = 1; + } else { + cmp = -1; + } - if(safe_str_eq(op, "defined")) { - if(h_val != NULL) { accept = TRUE; } + if(safe_str_eq(op, "defined")) { + if(h_val != NULL) { accept = TRUE; } - } else if(safe_str_eq(op, "not_defined")) { - if(h_val == NULL) { accept = TRUE; } + } else if(safe_str_eq(op, "not_defined")) { + if(h_val == NULL) { accept = TRUE; } - } else if(safe_str_eq(op, "eq")) { - if((h_val == value) || cmp == 0) { - accept = TRUE; - } + } else if(safe_str_eq(op, "eq")) { + if((h_val == value) || cmp == 0) { + accept = TRUE; + } - } else if(safe_str_eq(op, "ne")) { - if((h_val == NULL && value != NULL) - || (h_val != NULL && value == NULL) - || cmp != 0) { - accept = TRUE; - } + } else if(safe_str_eq(op, "ne")) { + if((h_val == NULL && value != NULL) + || (h_val != NULL && value == NULL) + || cmp != 0) { + accept = TRUE; + } - } else if(value == NULL || h_val == NULL) { - /* the comparision is meaningless from this point on */ - accept = FALSE; + } else if(value == NULL || h_val == NULL) { + /* the comparision is meaningless from this point on */ + accept = FALSE; - } else if(safe_str_eq(op, "lt")) { - if(cmp < 0) { accept = TRUE; } + } else if(safe_str_eq(op, "lt")) { + if(cmp < 0) { accept = TRUE; } - } else if(safe_str_eq(op, "lte")) { - if(cmp <= 0) { accept = TRUE; } + } else if(safe_str_eq(op, "lte")) { + if(cmp <= 0) { accept = TRUE; } - } else if(safe_str_eq(op, "gt")) { - if(cmp > 0) { accept = TRUE; } + } else if(safe_str_eq(op, "gt")) { + if(cmp > 0) { accept = TRUE; } - } else if(safe_str_eq(op, "gte")) { - if(cmp >= 0) { accept = TRUE; } - } + } else if(safe_str_eq(op, "gte")) { + if(cmp >= 0) { accept = TRUE; } + } - return accept; + return accept; } /* As per the nethack rules: * * moon period = 29.53058 days ~= 30, year = 365.2422 days * days moon phase advances on first day of year compared to preceding year * = 365.2422 - 12*29.53058 ~= 11 * years in Metonic cycle (time until same phases fall on the same days of * the month) = 18.6 ~= 19 * moon phase on first day of year (epact) ~= (11*(year%19) + 29) % 30 * (29 as initial condition) * current phase in days = first day phase + days elapsed in year * 6 moons ~= 177 days * 177 ~= 8 reported phases * 22 * + 11/22 for rounding * * 0-7, with 0: new, 4: full */ static int phase_of_the_moon(ha_time_t *now) { - int epact, diy, goldn; + int epact, diy, goldn; - diy = now->yeardays; - goldn = (now->years % 19) + 1; - epact = (11 * goldn + 18) % 30; - if ((epact == 25 && goldn > 11) || epact == 24) - epact++; + diy = now->yeardays; + goldn = (now->years % 19) + 1; + epact = (11 * goldn + 18) % 30; + if ((epact == 25 && goldn > 11) || epact == 24) + epact++; - return( (((((diy + epact) * 6) + 11) % 177) / 22) & 7 ); + return( (((((diy + epact) * 6) + 11) % 177) / 22) & 7 ); } #define cron_check(xml_field, time_field) \ - value = crm_element_value(cron_spec, xml_field); \ - if(value != NULL) { \ - gboolean pass = TRUE; \ - decodeNVpair(value, '-', &value_low, &value_high); \ - if(value_low == NULL) { \ - value_low = crm_strdup(value); \ - } \ - value_low_i = crm_parse_int(value_low, "0"); \ - value_high_i = crm_parse_int(value_high, "-1"); \ - if(value_high_i < 0) { \ - if(value_low_i != time_field) { \ - pass = FALSE; \ - } \ - } else if(value_low_i > time_field) { \ - pass = FALSE; \ - } else if(value_high_i < time_field) { \ - pass = FALSE; \ - } \ - crm_free(value_low); \ - crm_free(value_high); \ - if(pass == FALSE) { \ - crm_debug("Condition '%s' in %s: failed", value, xml_field); \ - return pass; \ - } \ - crm_debug("Condition '%s' in %s: passed", value, xml_field); \ - } + value = crm_element_value(cron_spec, xml_field); \ + if(value != NULL) { \ + gboolean pass = TRUE; \ + decodeNVpair(value, '-', &value_low, &value_high); \ + if(value_low == NULL) { \ + value_low = crm_strdup(value); \ + } \ + value_low_i = crm_parse_int(value_low, "0"); \ + value_high_i = crm_parse_int(value_high, "-1"); \ + if(value_high_i < 0) { \ + if(value_low_i != time_field) { \ + pass = FALSE; \ + } \ + } else if(value_low_i > time_field) { \ + pass = FALSE; \ + } else if(value_high_i < time_field) { \ + pass = FALSE; \ + } \ + crm_free(value_low); \ + crm_free(value_high); \ + if(pass == FALSE) { \ + crm_debug("Condition '%s' in %s: failed", value, xml_field); \ + return pass; \ + } \ + crm_debug("Condition '%s' in %s: passed", value, xml_field); \ + } gboolean cron_range_satisfied(ha_time_t *now, xmlNode *cron_spec) { - const char *value = NULL; - char *value_low = NULL; - char *value_high = NULL; + const char *value = NULL; + char *value_low = NULL; + char *value_high = NULL; - int value_low_i = 0; - int value_high_i = 0; + int value_low_i = 0; + int value_high_i = 0; - CRM_CHECK(now != NULL, return FALSE); + CRM_CHECK(now != NULL, return FALSE); - cron_check("seconds", now->seconds); - cron_check("minutes", now->minutes); - cron_check("hours", now->hours); - cron_check("monthdays", now->days); - cron_check("weekdays", now->weekdays); - cron_check("yeardays", now->yeardays); - cron_check("weeks", now->weeks); - cron_check("months", now->months); - cron_check("years", now->years); - cron_check("weekyears", now->weekyears); - cron_check("moon", phase_of_the_moon(now)); + cron_check("seconds", now->seconds); + cron_check("minutes", now->minutes); + cron_check("hours", now->hours); + cron_check("monthdays", now->days); + cron_check("weekdays", now->weekdays); + cron_check("yeardays", now->yeardays); + cron_check("weeks", now->weeks); + cron_check("months", now->months); + cron_check("years", now->years); + cron_check("weekyears", now->weekyears); + cron_check("moon", phase_of_the_moon(now)); - return TRUE; + return TRUE; } -#define update_field(xml_field, time_fn) \ - value = crm_element_value(duration_spec, xml_field); \ - if(value != NULL) { \ - int value_i = crm_parse_int(value, "0"); \ - time_fn(end, value_i); \ - } +#define update_field(xml_field, time_fn) \ + value = crm_element_value(duration_spec, xml_field); \ + if(value != NULL) { \ + int value_i = crm_parse_int(value, "0"); \ + time_fn(end, value_i); \ + } ha_time_t * parse_xml_duration(ha_time_t *start, xmlNode *duration_spec) { - ha_time_t *end = NULL; - const char *value = NULL; - - end = new_ha_date(FALSE); - ha_set_time(end, start, TRUE); - - update_field("years", add_years); - update_field("months", add_months); - update_field("weeks", add_weeks); - update_field("days", add_days); - update_field("hours", add_hours); - update_field("minutes", add_minutes); - update_field("seconds", add_seconds); + ha_time_t *end = NULL; + const char *value = NULL; + + end = new_ha_date(FALSE); + ha_set_time(end, start, TRUE); + + update_field("years", add_years); + update_field("months", add_months); + update_field("weeks", add_weeks); + update_field("days", add_days); + update_field("hours", add_hours); + update_field("minutes", add_minutes); + update_field("seconds", add_seconds); - return end; + return end; } gboolean test_date_expression(xmlNode *time_expr, ha_time_t *now) { - ha_time_t *start = NULL; - ha_time_t *end = NULL; - const char *value = NULL; - char *value_copy = NULL; - char *value_copy_start = NULL; - const char *op = crm_element_value(time_expr, "operation"); + ha_time_t *start = NULL; + ha_time_t *end = NULL; + const char *value = NULL; + char *value_copy = NULL; + char *value_copy_start = NULL; + const char *op = crm_element_value(time_expr, "operation"); - xmlNode *duration_spec = NULL; - xmlNode *date_spec = NULL; + xmlNode *duration_spec = NULL; + xmlNode *date_spec = NULL; - gboolean passed = FALSE; + gboolean passed = FALSE; - crm_debug_2("Testing expression: %s", ID(time_expr)); + crm_debug_2("Testing expression: %s", ID(time_expr)); - duration_spec = first_named_child(time_expr, "duration"); - date_spec = first_named_child(time_expr, "date_spec"); + duration_spec = first_named_child(time_expr, "duration"); + date_spec = first_named_child(time_expr, "date_spec"); - value = crm_element_value(time_expr, "start"); - if(value != NULL) { - value_copy = crm_strdup(value); - value_copy_start = value_copy; - start = parse_date(&value_copy); - crm_free(value_copy_start); - } - value = crm_element_value(time_expr, "end"); - if(value != NULL) { - value_copy = crm_strdup(value); - value_copy_start = value_copy; - end = parse_date(&value_copy); - crm_free(value_copy_start); - } - - if(start != NULL && end == NULL && duration_spec != NULL) { - end = parse_xml_duration(start, duration_spec); - } - if(op == NULL) { - op = "in_range"; - } + value = crm_element_value(time_expr, "start"); + if(value != NULL) { + value_copy = crm_strdup(value); + value_copy_start = value_copy; + start = parse_date(&value_copy); + crm_free(value_copy_start); + } + value = crm_element_value(time_expr, "end"); + if(value != NULL) { + value_copy = crm_strdup(value); + value_copy_start = value_copy; + end = parse_date(&value_copy); + crm_free(value_copy_start); + } + + if(start != NULL && end == NULL && duration_spec != NULL) { + end = parse_xml_duration(start, duration_spec); + } + if(op == NULL) { + op = "in_range"; + } - if(safe_str_eq(op, "date_spec") || safe_str_eq(op, "in_range")) { - if(start != NULL && compare_date(start, now) > 0) { - passed = FALSE; - } else if(end != NULL && compare_date(end, now) < 0) { - passed = FALSE; - } else if(safe_str_eq(op, "in_range")) { - passed = TRUE; - } else { - passed = cron_range_satisfied(now, date_spec); - } + if(safe_str_eq(op, "date_spec") || safe_str_eq(op, "in_range")) { + if(start != NULL && compare_date(start, now) > 0) { + passed = FALSE; + } else if(end != NULL && compare_date(end, now) < 0) { + passed = FALSE; + } else if(safe_str_eq(op, "in_range")) { + passed = TRUE; + } else { + passed = cron_range_satisfied(now, date_spec); + } - } else if(safe_str_eq(op, "gt") && compare_date(start, now) < 0) { - passed = TRUE; + } else if(safe_str_eq(op, "gt") && compare_date(start, now) < 0) { + passed = TRUE; - } else if(safe_str_eq(op, "lt") && compare_date(end, now) > 0) { - passed = TRUE; + } else if(safe_str_eq(op, "lt") && compare_date(end, now) > 0) { + passed = TRUE; - } else if(safe_str_eq(op, "eq") && compare_date(start, now) == 0) { - passed = TRUE; + } else if(safe_str_eq(op, "eq") && compare_date(start, now) == 0) { + passed = TRUE; - } else if(safe_str_eq(op, "neq") && compare_date(start, now) != 0) { - passed = TRUE; - } + } else if(safe_str_eq(op, "neq") && compare_date(start, now) != 0) { + passed = TRUE; + } - free_ha_date(start); - free_ha_date(end); - return passed; + free_ha_date(start); + free_ha_date(end); + return passed; } typedef struct sorted_set_s { - int score; - const char *name; - const char *special_name; - xmlNode *attr_set; + int score; + const char *name; + const char *special_name; + xmlNode *attr_set; } sorted_set_t; static gint sort_pairs(gconstpointer a, gconstpointer b) { - const sorted_set_t *pair_a = a; - const sorted_set_t *pair_b = b; + const sorted_set_t *pair_a = a; + const sorted_set_t *pair_b = b; - if(a == NULL && b == NULL) { - return 0; - } else if(a == NULL) { - return 1; - } else if(b == NULL) { - return -1; - } - - if(safe_str_eq(pair_a->name, pair_a->special_name)) { - return -1; - - } else if(safe_str_eq(pair_b->name, pair_a->special_name)) { - return 1; - } - - if(pair_a->score < pair_b->score) { - return 1; - } else if(pair_a->score > pair_b->score) { - return -1; - } + if(a == NULL && b == NULL) { return 0; + } else if(a == NULL) { + return 1; + } else if(b == NULL) { + return -1; + } + + if(safe_str_eq(pair_a->name, pair_a->special_name)) { + return -1; + + } else if(safe_str_eq(pair_b->name, pair_a->special_name)) { + return 1; + } + + if(pair_a->score < pair_b->score) { + return 1; + } else if(pair_a->score > pair_b->score) { + return -1; + } + return 0; } static void populate_hash(xmlNode *nvpair_list, GHashTable *hash, gboolean overwrite) { - const char *name = NULL; - const char *value = NULL; - const char *old_value = NULL; - xmlNode *list = nvpair_list; - - name = crm_element_name(list->children); - if(safe_str_eq(XML_TAG_ATTRS, name)) { - list = list->children; - } - - xml_child_iter_filter( - list, an_attr, XML_CIB_TAG_NVPAIR, - - name = crm_element_value(an_attr, XML_NVPAIR_ATTR_NAME); + const char *name = NULL; + const char *value = NULL; + const char *old_value = NULL; + xmlNode *list = nvpair_list; + xmlNode *an_attr = NULL; + + name = crm_element_name(list->children); + if(safe_str_eq(XML_TAG_ATTRS, name)) { + list = list->children; + } + + for(an_attr = list; an_attr != NULL; an_attr = an_attr->next) { + if(crm_str_eq((const char *)an_attr->name, XML_CIB_TAG_NVPAIR, TRUE)) { + name = crm_element_value(an_attr, XML_NVPAIR_ATTR_NAME); - crm_debug_4("Setting attribute: %s", name); - value = crm_element_value( - an_attr, XML_NVPAIR_ATTR_VALUE); + crm_debug_4("Setting attribute: %s", name); + value = crm_element_value( + an_attr, XML_NVPAIR_ATTR_VALUE); - if(name == NULL || value == NULL) { - continue; + if(name == NULL || value == NULL) { + continue; - } + } - old_value = g_hash_table_lookup(hash, name); + old_value = g_hash_table_lookup(hash, name); - if(safe_str_eq(value, "#default")) { - if(old_value) { - crm_debug_2("Removing value for %s (%s)", name, value); - g_hash_table_remove(hash, name); - } - continue; - - } else if(old_value == NULL) { - g_hash_table_insert(hash, crm_strdup(name), crm_strdup(value)); - - } else if(overwrite) { - crm_debug("Overwriting value of %s: %s -> %s", name, old_value, value); - g_hash_table_replace(hash, crm_strdup(name), crm_strdup(value)); + if(safe_str_eq(value, "#default")) { + if(old_value) { + crm_debug_2("Removing value for %s (%s)", name, value); + g_hash_table_remove(hash, name); } - - ); + continue; + + } else if(old_value == NULL) { + g_hash_table_insert(hash, crm_strdup(name), crm_strdup(value)); + + } else if(overwrite) { + crm_debug("Overwriting value of %s: %s -> %s", name, old_value, value); + g_hash_table_replace(hash, crm_strdup(name), crm_strdup(value)); + } + } + } } struct unpack_data_s { gboolean overwrite; GHashTable *node_hash; GHashTable *hash; ha_time_t *now; }; static void unpack_attr_set(gpointer data, gpointer user_data) { - sorted_set_t *pair = data; - struct unpack_data_s *unpack_data = user_data; + sorted_set_t *pair = data; + struct unpack_data_s *unpack_data = user_data; - if(test_ruleset(pair->attr_set, - unpack_data->node_hash, unpack_data->now) == FALSE) { - return; - } + if(test_ruleset(pair->attr_set, + unpack_data->node_hash, unpack_data->now) == FALSE) { + return; + } - crm_debug_3("Adding attributes from %s", pair->name); - populate_hash(pair->attr_set, unpack_data->hash, unpack_data->overwrite); + crm_debug_3("Adding attributes from %s", pair->name); + populate_hash(pair->attr_set, unpack_data->hash, unpack_data->overwrite); } void unpack_instance_attributes( - xmlNode *top, xmlNode *xml_obj, const char *set_name, GHashTable *node_hash, - GHashTable *hash, const char *always_first, gboolean overwrite, ha_time_t *now) + xmlNode *top, xmlNode *xml_obj, const char *set_name, GHashTable *node_hash, + GHashTable *hash, const char *always_first, gboolean overwrite, ha_time_t *now) { - GListPtr sorted = NULL; - GListPtr unsorted = NULL; - const char *score = NULL; - sorted_set_t *pair = NULL; - struct unpack_data_s data; + GListPtr sorted = NULL; + GListPtr unsorted = NULL; + const char *score = NULL; + sorted_set_t *pair = NULL; + struct unpack_data_s data; + xmlNode *attr_set = NULL; - if(xml_obj == NULL) { - crm_debug_4("No instance attributes"); - return; - } + if(xml_obj == NULL) { + crm_debug_4("No instance attributes"); + return; + } + + crm_debug_4("Checking for attributes"); + for(attr_set = xml_obj; attr_set != NULL; attr_set = attr_set->next) { + if(crm_str_eq((const char *)attr_set->name, set_name, TRUE)) { + pair = NULL; + attr_set = expand_idref(attr_set, top); + if(attr_set == NULL) { + continue; + } + + crm_malloc0(pair, sizeof(sorted_set_t)); + pair->name = ID(attr_set); + pair->special_name = always_first; + pair->attr_set = attr_set; - crm_debug_4("Checking for attributes"); - xml_child_iter_filter( - xml_obj, attr_set, set_name, + score = crm_element_value(attr_set, XML_RULE_ATTR_SCORE); + pair->score = char2score(score); - pair = NULL; - attr_set = expand_idref(attr_set, top); - if(attr_set == NULL) { - continue; - } - - crm_malloc0(pair, sizeof(sorted_set_t)); - pair->name = ID(attr_set); - pair->special_name = always_first; - pair->attr_set = attr_set; - - score = crm_element_value(attr_set, XML_RULE_ATTR_SCORE); - pair->score = char2score(score); - - unsorted = g_list_prepend(unsorted, pair); - ); - - if(pair != NULL) { - data.hash = hash; - data.node_hash = node_hash; - data.now = now; - data.overwrite = overwrite; + unsorted = g_list_prepend(unsorted, pair); } + } + + if(pair != NULL) { + data.hash = hash; + data.node_hash = node_hash; + data.now = now; + data.overwrite = overwrite; + } - sorted = g_list_sort(unsorted, sort_pairs); - g_list_foreach(sorted, unpack_attr_set, &data); - slist_destroy(sorted_set_t, child, sorted, crm_free(child)); + sorted = g_list_sort(unsorted, sort_pairs); + g_list_foreach(sorted, unpack_attr_set, &data); + slist_basic_destroy(sorted); } - diff --git a/lib/pengine/status.c b/lib/pengine/status.c index 1f1c2e3253..47177a9603 100644 --- a/lib/pengine/status.c +++ b/lib/pengine/status.c @@ -1,286 +1,286 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include extern xmlNode*get_object_root( const char *object_type, xmlNode *the_root); #define MEMCHECK_STAGE_0 0 #define check_and_exit(stage) cleanup_calculations(data_set); \ crm_mem_stats(NULL); \ crm_err("Exiting: stage %d", stage); \ exit(1); /* * Unpack everything * At the end you'll have: * - A list of nodes * - A list of resources (each with any dependencies on other resources) * - A list of constraints between resources and nodes * - A list of constraints between start/stop actions * - A list of nodes that need to be stonith'd * - A list of nodes that need to be shutdown * - A list of the possible stop/start actions (without dependencies) */ gboolean cluster_status(pe_working_set_t *data_set) { xmlNode * config = get_object_root( XML_CIB_TAG_CRMCONFIG, data_set->input); xmlNode * cib_nodes = get_object_root( XML_CIB_TAG_NODES, data_set->input); xmlNode * cib_resources = get_object_root( XML_CIB_TAG_RESOURCES, data_set->input); xmlNode * cib_status = get_object_root( XML_CIB_TAG_STATUS, data_set->input); xmlNode * cib_domains = get_object_root( XML_CIB_TAG_DOMAINS, data_set->input); const char *value = crm_element_value( data_set->input, XML_ATTR_HAVE_QUORUM); crm_debug_3("Beginning unpack"); pe_dataset = data_set; /* reset remaining global variables */ data_set->failed = create_xml_node(NULL, "failed-ops"); if(data_set->input == NULL) { return FALSE; } if(data_set->now == NULL) { data_set->now = new_ha_date(TRUE); } if(data_set->input != NULL && crm_element_value(data_set->input, XML_ATTR_DC_UUID) != NULL) { /* this should always be present */ data_set->dc_uuid = crm_element_value_copy( data_set->input, XML_ATTR_DC_UUID); } clear_bit_inplace(data_set->flags, pe_flag_have_quorum); if(crm_is_true(value)) { set_bit_inplace(data_set->flags, pe_flag_have_quorum); } data_set->op_defaults = get_object_root(XML_CIB_TAG_OPCONFIG, data_set->input); data_set->rsc_defaults = get_object_root(XML_CIB_TAG_RSCCONFIG, data_set->input); unpack_config(config, data_set); if(is_set(data_set->flags, pe_flag_have_quorum) == FALSE && data_set->no_quorum_policy != no_quorum_ignore) { crm_warn("We do not have quorum" " - fencing and resource management disabled"); } unpack_nodes(cib_nodes, data_set); unpack_domains(cib_domains, data_set); unpack_resources(cib_resources, data_set); unpack_status(cib_status, data_set); set_bit_inplace(data_set->flags, pe_flag_have_status); return TRUE; } static void pe_free_resources(GListPtr resources) { resource_t *rsc = NULL; GListPtr iterator = resources; while(iterator != NULL) { iterator = iterator; rsc = (resource_t *)iterator->data; iterator = iterator->next; rsc->fns->free(rsc); } if(resources != NULL) { g_list_free(resources); } } static void pe_free_actions(GListPtr actions) { GListPtr iterator = actions; while(iterator != NULL) { pe_free_action(iterator->data); iterator = iterator->next; } if(actions != NULL) { g_list_free(actions); } } static void pe_free_nodes(GListPtr nodes) { GListPtr iterator = nodes; while(iterator != NULL) { node_t *node = (node_t*)iterator->data; struct node_shared_s *details = node->details; iterator = iterator->next; crm_debug_5("deleting node"); crm_debug_5("%s is being deleted", details->uname); print_node("delete", node, FALSE); if(details != NULL) { if(details->attrs != NULL) { g_hash_table_destroy(details->attrs); } if(details->utilization != NULL) { g_hash_table_destroy(details->utilization); } - pe_free_shallow_adv(details->running_rsc, FALSE); - pe_free_shallow_adv(details->allocated_rsc, FALSE); + g_list_free(details->running_rsc); + g_list_free(details->allocated_rsc); crm_free(details); } crm_free(node); } if(nodes != NULL) { g_list_free(nodes); } } void cleanup_calculations(pe_working_set_t *data_set) { pe_dataset = NULL; if(data_set == NULL) { return; } clear_bit_inplace(data_set->flags, pe_flag_have_status); if(data_set->config_hash != NULL) { g_hash_table_destroy(data_set->config_hash); } crm_free(data_set->dc_uuid); crm_debug_3("deleting resources"); pe_free_resources(data_set->resources); crm_debug_3("deleting actions"); pe_free_actions(data_set->actions); if(data_set->domains) { g_hash_table_destroy(data_set->domains); } crm_debug_3("deleting nodes"); pe_free_nodes(data_set->nodes); free_xml(data_set->graph); free_ha_date(data_set->now); free_xml(data_set->input); free_xml(data_set->failed); set_working_set_defaults(data_set); CRM_CHECK(data_set->ordering_constraints == NULL, ;); CRM_CHECK(data_set->placement_constraints == NULL, ;); xmlCleanupParser(); } void set_working_set_defaults(pe_working_set_t *data_set) { pe_dataset = data_set; memset(data_set, 0, sizeof(pe_working_set_t)); data_set->order_id = 1; data_set->action_id = 1; data_set->no_quorum_policy = no_quorum_freeze; data_set->flags = 0x0ULL; set_bit_inplace(data_set->flags, pe_flag_stop_rsc_orphans); set_bit_inplace(data_set->flags, pe_flag_symmetric_cluster); set_bit_inplace(data_set->flags, pe_flag_is_managed_default); set_bit_inplace(data_set->flags, pe_flag_stop_action_orphans); } resource_t * pe_find_resource(GListPtr rsc_list, const char *id) { unsigned lpc = 0; resource_t *rsc = NULL; resource_t *match = NULL; if(id == NULL) { return NULL; } for(lpc = 0; lpc < g_list_length(rsc_list); lpc++) { rsc = g_list_nth_data(rsc_list, lpc); match = rsc->fns->find_rsc(rsc, id, TRUE, FALSE, NULL, TRUE); if(match != NULL) { return match; } } crm_debug_2("No match for %s", id); return NULL; } node_t * pe_find_node_id(GListPtr nodes, const char *id) { GListPtr gIter = nodes; for(; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t*)gIter->data; if(node && safe_str_eq(node->details->id, id)) { return node; } } /* error */ return NULL; } node_t * pe_find_node(GListPtr nodes, const char *uname) { GListPtr gIter = nodes; for(; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t*)gIter->data; if(node && safe_str_eq(node->details->uname, uname)) { return node; } } /* error */ return NULL; } diff --git a/lib/pengine/unpack.c b/lib/pengine/unpack.c index 2e26d3a9a2..6e3497febb 100644 --- a/lib/pengine/unpack.c +++ b/lib/pengine/unpack.c @@ -1,2025 +1,2036 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include CRM_TRACE_INIT_DATA(pe_status); #define set_config_flag(data_set, option, flag) do { \ const char *tmp = pe_pref(data_set->config_hash, option); \ if(tmp) { \ if(crm_is_true(tmp)) { \ set_bit_inplace(data_set->flags, flag); \ } else { \ clear_bit_inplace(data_set->flags, flag); \ } \ } \ } while(0) gboolean unpack_rsc_op( resource_t *rsc, node_t *node, xmlNode *xml_op, GListPtr next, enum action_fail_response *failed, pe_working_set_t *data_set); static void pe_fence_node(pe_working_set_t *data_set, node_t *node, const char *reason) { CRM_CHECK(node, return); if(node->details->unclean == FALSE) { if(is_set(data_set->flags, pe_flag_stonith_enabled)) { crm_warn("Node %s will be fenced %s", node->details->uname, reason); } else { crm_warn("Node %s is unclean %s", node->details->uname, reason); } } node->details->unclean = TRUE; } gboolean unpack_config(xmlNode *config, pe_working_set_t *data_set) { - const char *value = NULL; - GHashTable *config_hash = g_hash_table_new_full( - g_str_hash,g_str_equal, g_hash_destroy_str,g_hash_destroy_str); + const char *value = NULL; + GHashTable *config_hash = g_hash_table_new_full( + g_str_hash,g_str_equal, g_hash_destroy_str,g_hash_destroy_str); - data_set->config_hash = config_hash; + data_set->config_hash = config_hash; - unpack_instance_attributes( - data_set->input, config, XML_CIB_TAG_PROPSET, NULL, config_hash, - CIB_OPTIONS_FIRST, FALSE, data_set->now); + unpack_instance_attributes( + data_set->input, config, XML_CIB_TAG_PROPSET, NULL, config_hash, + CIB_OPTIONS_FIRST, FALSE, data_set->now); - verify_pe_options(data_set->config_hash); + verify_pe_options(data_set->config_hash); - set_config_flag(data_set, "enable-startup-probes", pe_flag_startup_probes); - crm_info("Startup probes: %s", - is_set(data_set->flags, pe_flag_startup_probes)?"enabled":"disabled (dangerous)"); + set_config_flag(data_set, "enable-startup-probes", pe_flag_startup_probes); + crm_info("Startup probes: %s", + is_set(data_set->flags, pe_flag_startup_probes)?"enabled":"disabled (dangerous)"); - value = pe_pref(data_set->config_hash, "stonith-timeout"); - data_set->stonith_timeout = crm_get_msec(value); - crm_debug("STONITH timeout: %d", data_set->stonith_timeout); + value = pe_pref(data_set->config_hash, "stonith-timeout"); + data_set->stonith_timeout = crm_get_msec(value); + crm_debug("STONITH timeout: %d", data_set->stonith_timeout); - set_config_flag(data_set, "stonith-enabled", pe_flag_stonith_enabled); - crm_debug("STONITH of failed nodes is %s", - is_set(data_set->flags, pe_flag_stonith_enabled)?"enabled":"disabled"); + set_config_flag(data_set, "stonith-enabled", pe_flag_stonith_enabled); + crm_debug("STONITH of failed nodes is %s", + is_set(data_set->flags, pe_flag_stonith_enabled)?"enabled":"disabled"); - data_set->stonith_action = pe_pref(data_set->config_hash, "stonith-action"); - crm_debug_2("STONITH will %s nodes", data_set->stonith_action); + data_set->stonith_action = pe_pref(data_set->config_hash, "stonith-action"); + crm_debug_2("STONITH will %s nodes", data_set->stonith_action); - set_config_flag(data_set, "stop-all-resources", pe_flag_stop_everything); - crm_debug("Stop all active resources: %s", - is_set(data_set->flags, pe_flag_stop_everything)?"true":"false"); + set_config_flag(data_set, "stop-all-resources", pe_flag_stop_everything); + crm_debug("Stop all active resources: %s", + is_set(data_set->flags, pe_flag_stop_everything)?"true":"false"); - set_config_flag(data_set, "symmetric-cluster", pe_flag_symmetric_cluster); - if(is_set(data_set->flags, pe_flag_symmetric_cluster)) { - crm_debug("Cluster is symmetric" - " - resources can run anywhere by default"); - } + set_config_flag(data_set, "symmetric-cluster", pe_flag_symmetric_cluster); + if(is_set(data_set->flags, pe_flag_symmetric_cluster)) { + crm_debug("Cluster is symmetric" + " - resources can run anywhere by default"); + } - value = pe_pref(data_set->config_hash, "default-resource-stickiness"); - data_set->default_resource_stickiness = char2score(value); - crm_debug("Default stickiness: %d", - data_set->default_resource_stickiness); + value = pe_pref(data_set->config_hash, "default-resource-stickiness"); + data_set->default_resource_stickiness = char2score(value); + crm_debug("Default stickiness: %d", + data_set->default_resource_stickiness); - value = pe_pref(data_set->config_hash, "no-quorum-policy"); + value = pe_pref(data_set->config_hash, "no-quorum-policy"); - if(safe_str_eq(value, "ignore")) { - data_set->no_quorum_policy = no_quorum_ignore; + if(safe_str_eq(value, "ignore")) { + data_set->no_quorum_policy = no_quorum_ignore; - } else if(safe_str_eq(value, "freeze")) { - data_set->no_quorum_policy = no_quorum_freeze; + } else if(safe_str_eq(value, "freeze")) { + data_set->no_quorum_policy = no_quorum_freeze; - } else if(safe_str_eq(value, "suicide")) { - gboolean do_panic = FALSE; - crm_element_value_int(data_set->input, XML_ATTR_QUORUM_PANIC, &do_panic); + } else if(safe_str_eq(value, "suicide")) { + gboolean do_panic = FALSE; + crm_element_value_int(data_set->input, XML_ATTR_QUORUM_PANIC, &do_panic); - if(is_set(data_set->flags, pe_flag_stonith_enabled) == FALSE){ - crm_config_err("Setting no-quorum-policy=suicide makes no sense if stonith-enabled=false"); - } + if(is_set(data_set->flags, pe_flag_stonith_enabled) == FALSE){ + crm_config_err("Setting no-quorum-policy=suicide makes no sense if stonith-enabled=false"); + } - if(do_panic && is_set(data_set->flags, pe_flag_stonith_enabled)) { - data_set->no_quorum_policy = no_quorum_suicide; + if(do_panic && is_set(data_set->flags, pe_flag_stonith_enabled)) { + data_set->no_quorum_policy = no_quorum_suicide; - } else if(is_set(data_set->flags, pe_flag_have_quorum) == FALSE && do_panic == FALSE) { - crm_notice("Resetting no-quorum-policy to 'stop': The cluster has never had quorum"); - data_set->no_quorum_policy = no_quorum_stop; - } - - } else { - data_set->no_quorum_policy = no_quorum_stop; + } else if(is_set(data_set->flags, pe_flag_have_quorum) == FALSE && do_panic == FALSE) { + crm_notice("Resetting no-quorum-policy to 'stop': The cluster has never had quorum"); + data_set->no_quorum_policy = no_quorum_stop; } + + } else { + data_set->no_quorum_policy = no_quorum_stop; + } - switch (data_set->no_quorum_policy) { - case no_quorum_freeze: - crm_debug("On loss of CCM Quorum: Freeze resources"); - break; - case no_quorum_stop: - crm_debug("On loss of CCM Quorum: Stop ALL resources"); - break; - case no_quorum_suicide: - crm_notice("On loss of CCM Quorum: Fence all remaining nodes"); - break; - case no_quorum_ignore: - crm_notice("On loss of CCM Quorum: Ignore"); - break; - } + switch (data_set->no_quorum_policy) { + case no_quorum_freeze: + crm_debug("On loss of CCM Quorum: Freeze resources"); + break; + case no_quorum_stop: + crm_debug("On loss of CCM Quorum: Stop ALL resources"); + break; + case no_quorum_suicide: + crm_notice("On loss of CCM Quorum: Fence all remaining nodes"); + break; + case no_quorum_ignore: + crm_notice("On loss of CCM Quorum: Ignore"); + break; + } - set_config_flag(data_set, "stop-orphan-resources", pe_flag_stop_rsc_orphans); - crm_debug_2("Orphan resources are %s", - is_set(data_set->flags, pe_flag_stop_rsc_orphans)?"stopped":"ignored"); + set_config_flag(data_set, "stop-orphan-resources", pe_flag_stop_rsc_orphans); + crm_debug_2("Orphan resources are %s", + is_set(data_set->flags, pe_flag_stop_rsc_orphans)?"stopped":"ignored"); - set_config_flag(data_set, "stop-orphan-actions", pe_flag_stop_action_orphans); - crm_debug_2("Orphan resource actions are %s", - is_set(data_set->flags, pe_flag_stop_action_orphans)?"stopped":"ignored"); + set_config_flag(data_set, "stop-orphan-actions", pe_flag_stop_action_orphans); + crm_debug_2("Orphan resource actions are %s", + is_set(data_set->flags, pe_flag_stop_action_orphans)?"stopped":"ignored"); - set_config_flag(data_set, "remove-after-stop", pe_flag_remove_after_stop); - crm_debug_2("Stopped resources are removed from the status section: %s", - is_set(data_set->flags, pe_flag_remove_after_stop)?"true":"false"); + set_config_flag(data_set, "remove-after-stop", pe_flag_remove_after_stop); + crm_debug_2("Stopped resources are removed from the status section: %s", + is_set(data_set->flags, pe_flag_remove_after_stop)?"true":"false"); - set_config_flag(data_set, "maintenance-mode", pe_flag_maintenance_mode); - crm_debug_2("Maintenance mode: %s", - is_set(data_set->flags, pe_flag_maintenance_mode)?"true":"false"); + set_config_flag(data_set, "maintenance-mode", pe_flag_maintenance_mode); + crm_debug_2("Maintenance mode: %s", + is_set(data_set->flags, pe_flag_maintenance_mode)?"true":"false"); - if(is_set(data_set->flags, pe_flag_maintenance_mode)) { - clear_bit(data_set->flags, pe_flag_is_managed_default); - } else { - set_config_flag(data_set, "is-managed-default", pe_flag_is_managed_default); - } - crm_debug_2("By default resources are %smanaged", - is_set(data_set->flags, pe_flag_is_managed_default)?"":"not "); + if(is_set(data_set->flags, pe_flag_maintenance_mode)) { + clear_bit(data_set->flags, pe_flag_is_managed_default); + } else { + set_config_flag(data_set, "is-managed-default", pe_flag_is_managed_default); + } + crm_debug_2("By default resources are %smanaged", + is_set(data_set->flags, pe_flag_is_managed_default)?"":"not "); - set_config_flag(data_set, "start-failure-is-fatal", pe_flag_start_failure_fatal); - crm_debug_2("Start failures are %s", - is_set(data_set->flags, pe_flag_start_failure_fatal)?"always fatal":"handled by failcount"); + set_config_flag(data_set, "start-failure-is-fatal", pe_flag_start_failure_fatal); + crm_debug_2("Start failures are %s", + is_set(data_set->flags, pe_flag_start_failure_fatal)?"always fatal":"handled by failcount"); - node_score_red = char2score(pe_pref(data_set->config_hash, "node-health-red")); - node_score_green = char2score(pe_pref(data_set->config_hash, "node-health-green")); - node_score_yellow = char2score(pe_pref(data_set->config_hash, "node-health-yellow")); + node_score_red = char2score(pe_pref(data_set->config_hash, "node-health-red")); + node_score_green = char2score(pe_pref(data_set->config_hash, "node-health-green")); + node_score_yellow = char2score(pe_pref(data_set->config_hash, "node-health-yellow")); - crm_info("Node scores: 'red' = %s, 'yellow' = %s, 'green' = %s", - pe_pref(data_set->config_hash, "node-health-red"), - pe_pref(data_set->config_hash, "node-health-yellow"), - pe_pref(data_set->config_hash, "node-health-green")); + crm_info("Node scores: 'red' = %s, 'yellow' = %s, 'green' = %s", + pe_pref(data_set->config_hash, "node-health-red"), + pe_pref(data_set->config_hash, "node-health-yellow"), + pe_pref(data_set->config_hash, "node-health-green")); - data_set->placement_strategy = pe_pref(data_set->config_hash, "placement-strategy"); - crm_debug_2("Placement strategy: %s", data_set->placement_strategy); + data_set->placement_strategy = pe_pref(data_set->config_hash, "placement-strategy"); + crm_debug_2("Placement strategy: %s", data_set->placement_strategy); - return TRUE; + return TRUE; } gboolean unpack_nodes(xmlNode * xml_nodes, pe_working_set_t *data_set) { - node_t *new_node = NULL; - const char *id = NULL; - const char *uname = NULL; - const char *type = NULL; - const char *score = NULL; - gboolean unseen_are_unclean = TRUE; - const char *blind_faith = pe_pref( - data_set->config_hash, "startup-fencing"); + xmlNode *xml_obj = NULL; + node_t *new_node = NULL; + const char *id = NULL; + const char *uname = NULL; + const char *type = NULL; + const char *score = NULL; + gboolean unseen_are_unclean = TRUE; + const char *blind_faith = pe_pref( + data_set->config_hash, "startup-fencing"); - if(crm_is_true(blind_faith) == FALSE) { - unseen_are_unclean = FALSE; - crm_warn("Blind faith: not fencing unseen nodes"); - } - - xml_child_iter_filter( - xml_nodes, xml_obj, XML_CIB_TAG_NODE, + if(crm_is_true(blind_faith) == FALSE) { + unseen_are_unclean = FALSE; + crm_warn("Blind faith: not fencing unseen nodes"); + } - new_node = NULL; + for(xml_obj = xml_nodes; xml_obj != NULL; xml_obj = xml_obj->next) { + if(crm_str_eq((const char *)xml_obj->name, XML_CIB_TAG_NODE, TRUE)) { + new_node = NULL; - id = crm_element_value(xml_obj, XML_ATTR_ID); - uname = crm_element_value(xml_obj, XML_ATTR_UNAME); - type = crm_element_value(xml_obj, XML_ATTR_TYPE); - score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE); - crm_debug_3("Processing node %s/%s", uname, id); + id = crm_element_value(xml_obj, XML_ATTR_ID); + uname = crm_element_value(xml_obj, XML_ATTR_UNAME); + type = crm_element_value(xml_obj, XML_ATTR_TYPE); + score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE); + crm_debug_3("Processing node %s/%s", uname, id); - if(id == NULL) { - crm_config_err("Must specify id tag in "); - continue; - } - if(type == NULL) { - crm_config_err("Must specify type tag in "); - continue; - } - if(pe_find_node(data_set->nodes, uname) != NULL) { - crm_config_warn("Detected multiple node entries with uname=%s" - " - this is rarely intended", uname); - } + if(id == NULL) { + crm_config_err("Must specify id tag in "); + continue; + } + if(type == NULL) { + crm_config_err("Must specify type tag in "); + continue; + } + if(pe_find_node(data_set->nodes, uname) != NULL) { + crm_config_warn("Detected multiple node entries with uname=%s" + " - this is rarely intended", uname); + } - crm_malloc0(new_node, sizeof(node_t)); - if(new_node == NULL) { - return FALSE; - } + crm_malloc0(new_node, sizeof(node_t)); + if(new_node == NULL) { + return FALSE; + } - new_node->weight = char2score(score); - new_node->fixed = FALSE; - crm_malloc0(new_node->details, - sizeof(struct node_shared_s)); - - if(new_node->details == NULL) { - crm_free(new_node); - return FALSE; - } + new_node->weight = char2score(score); + new_node->fixed = FALSE; + crm_malloc0(new_node->details, + sizeof(struct node_shared_s)); + + if(new_node->details == NULL) { + crm_free(new_node); + return FALSE; + } - crm_debug_3("Creaing node for entry %s/%s", uname, id); - new_node->details->id = id; - new_node->details->uname = uname; - new_node->details->type = node_ping; - new_node->details->online = FALSE; - new_node->details->shutdown = FALSE; - new_node->details->running_rsc = NULL; - new_node->details->attrs = g_hash_table_new_full( - g_str_hash, g_str_equal, - g_hash_destroy_str, g_hash_destroy_str); - new_node->details->utilization = g_hash_table_new_full( - g_str_hash, g_str_equal, - g_hash_destroy_str, g_hash_destroy_str); + crm_debug_3("Creaing node for entry %s/%s", uname, id); + new_node->details->id = id; + new_node->details->uname = uname; + new_node->details->type = node_ping; + new_node->details->online = FALSE; + new_node->details->shutdown = FALSE; + new_node->details->running_rsc = NULL; + new_node->details->attrs = g_hash_table_new_full( + g_str_hash, g_str_equal, + g_hash_destroy_str, g_hash_destroy_str); + new_node->details->utilization = g_hash_table_new_full( + g_str_hash, g_str_equal, + g_hash_destroy_str, g_hash_destroy_str); /* if(data_set->have_quorum == FALSE */ /* && data_set->no_quorum_policy == no_quorum_stop) { */ /* /\* start shutting resources down *\/ */ /* new_node->weight = -INFINITY; */ /* } */ - if(is_set(data_set->flags, pe_flag_stonith_enabled) == FALSE || unseen_are_unclean == FALSE) { - /* blind faith... */ - new_node->details->unclean = FALSE; + if(is_set(data_set->flags, pe_flag_stonith_enabled) == FALSE || unseen_are_unclean == FALSE) { + /* blind faith... */ + new_node->details->unclean = FALSE; - } else { - /* all nodes are unclean until we've seen their - * status entry - */ - new_node->details->unclean = TRUE; - } + } else { + /* all nodes are unclean until we've seen their + * status entry + */ + new_node->details->unclean = TRUE; + } - if(type == NULL - || safe_str_eq(type, "member") - || safe_str_eq(type, NORMALNODE)) { - new_node->details->type = node_member; - } + if(type == NULL + || safe_str_eq(type, "member") + || safe_str_eq(type, NORMALNODE)) { + new_node->details->type = node_member; + } - add_node_attrs(xml_obj, new_node, FALSE, data_set); - unpack_instance_attributes( - data_set->input, xml_obj, XML_TAG_UTILIZATION, NULL, - new_node->details->utilization, NULL, FALSE, data_set->now); + add_node_attrs(xml_obj, new_node, FALSE, data_set); + unpack_instance_attributes( + data_set->input, xml_obj, XML_TAG_UTILIZATION, NULL, + new_node->details->utilization, NULL, FALSE, data_set->now); - data_set->nodes = g_list_append(data_set->nodes, new_node); - crm_debug_3("Done with node %s", - crm_element_value(xml_obj, XML_ATTR_UNAME)); - ); - - return TRUE; + data_set->nodes = g_list_append(data_set->nodes, new_node); + crm_debug_3("Done with node %s", + crm_element_value(xml_obj, XML_ATTR_UNAME)); + } + } + + return TRUE; } static void g_hash_destroy_node_list(gpointer data) { GListPtr domain = data; - slist_destroy(node_t, node, domain, crm_free(node)); + slist_basic_destroy(domain); } gboolean unpack_domains(xmlNode *xml_domains, pe_working_set_t *data_set) { + const char *id = NULL; GListPtr domain = NULL; - const char *id = NULL; + xmlNode *xml_node = NULL; + xmlNode *xml_domain = NULL; crm_info("Unpacking domains"); data_set->domains = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_node_list); - xml_child_iter_filter( - xml_domains, xml_domain, XML_CIB_TAG_DOMAIN, + for(xml_domain = xml_domains; xml_domain != NULL; xml_domain = xml_domain->next) { + if(crm_str_eq((const char *)xml_domain->name, XML_CIB_TAG_DOMAIN, TRUE)) { + domain = NULL; + id = crm_element_value(xml_domain, XML_ATTR_ID); + + for(xml_node = xml_domain; xml_node != NULL; xml_node = xml_node->next) { + if(crm_str_eq((const char *)xml_node->name, XML_CIB_TAG_NODE, TRUE)) { + node_t *copy = NULL; + node_t *node = NULL; + const char *uname = crm_element_value(xml_node, "name"); + const char *score = crm_element_value(xml_node, XML_RULE_ATTR_SCORE); + + if(uname == NULL) { + crm_config_err("Invalid domain %s: Must specify id tag in ", id); + continue; + } - domain = NULL; - id = crm_element_value(xml_domain, XML_ATTR_ID); + node = pe_find_node(data_set->nodes, uname); + if(node == NULL) { + node = pe_find_node_id(data_set->nodes, uname); + } + if(node == NULL) { + crm_config_warn("Invalid domain %s: Node %s does not exist", id, uname); + continue; + } - xml_child_iter_filter( - xml_domain, xml_node, XML_CIB_TAG_NODE, - - node_t *copy = NULL; - node_t *node = NULL; - const char *uname = crm_element_value(xml_node, "name"); - const char *score = crm_element_value(xml_node, XML_RULE_ATTR_SCORE); + copy = node_copy(node); + copy->weight = char2score(score); + crm_debug("Adding %s to domain %s with score %s", node->details->uname, id, score); - if(uname == NULL) { - crm_config_err("Invalid domain %s: Must specify id tag in ", id); - continue; - } - - node = pe_find_node(data_set->nodes, uname); - if(node == NULL) { - node = pe_find_node_id(data_set->nodes, uname); + domain = g_list_prepend(domain, copy); + } } - if(node == NULL) { - crm_config_warn("Invalid domain %s: Node %s does not exist", id, uname); - continue; + + if(domain) { + crm_debug("Created domain %s with %d members", id, g_list_length(domain)); + g_hash_table_replace(data_set->domains, crm_strdup(id), domain); } - - copy = node_copy(node); - copy->weight = char2score(score); - crm_debug("Adding %s to domain %s with score %s", node->details->uname, id, score); - - domain = g_list_prepend(domain, copy); - ); - - if(domain) { - crm_debug("Created domain %s with %d members", id, g_list_length(domain)); - g_hash_table_replace(data_set->domains, crm_strdup(id), domain); } - ); - - return TRUE; + } + + return TRUE; } gboolean unpack_resources(xmlNode * xml_resources, pe_working_set_t *data_set) { - xml_child_iter( - xml_resources, xml_obj, - - resource_t *new_rsc = NULL; - crm_debug_3("Beginning unpack... <%s id=%s... >", - crm_element_name(xml_obj), ID(xml_obj)); - if(common_unpack(xml_obj, &new_rsc, NULL, data_set)) { - data_set->resources = g_list_append( - data_set->resources, new_rsc); + xmlNode *xml_obj = NULL; + for(xml_obj = xml_resources; xml_obj != NULL; xml_obj = xml_obj->next) { + resource_t *new_rsc = NULL; + crm_debug_3("Beginning unpack... <%s id=%s... >", + crm_element_name(xml_obj), ID(xml_obj)); + if(common_unpack(xml_obj, &new_rsc, NULL, data_set)) { + data_set->resources = g_list_append( + data_set->resources, new_rsc); - print_resource(LOG_DEBUG_3, "Added", new_rsc, FALSE); - - } else { - crm_config_err("Failed unpacking %s %s", - crm_element_name(xml_obj), - crm_element_value(xml_obj, XML_ATTR_ID)); - if(new_rsc != NULL && new_rsc->fns != NULL) { - new_rsc->fns->free(new_rsc); - } - } - ); - - data_set->resources = g_list_sort( - data_set->resources, sort_rsc_priority); + print_resource(LOG_DEBUG_3, "Added", new_rsc, FALSE); - if(is_set(data_set->flags, pe_flag_stonith_enabled) && is_set(data_set->flags, pe_flag_have_stonith_resource) == FALSE) { - crm_config_err("Resource start-up disabled since no STONITH resources have been defined"); - crm_config_err("Either configure some or disable STONITH with the stonith-enabled option"); - crm_config_err("NOTE: Clusters with shared data need STONITH to ensure data integrity"); + } else { + crm_config_err("Failed unpacking %s %s", + crm_element_name(xml_obj), + crm_element_value(xml_obj, XML_ATTR_ID)); + if(new_rsc != NULL && new_rsc->fns != NULL) { + new_rsc->fns->free(new_rsc); + } } + } - return TRUE; + data_set->resources = g_list_sort( + data_set->resources, sort_rsc_priority); + + if(is_set(data_set->flags, pe_flag_stonith_enabled) && is_set(data_set->flags, pe_flag_have_stonith_resource) == FALSE) { + crm_config_err("Resource start-up disabled since no STONITH resources have been defined"); + crm_config_err("Either configure some or disable STONITH with the stonith-enabled option"); + crm_config_err("NOTE: Clusters with shared data need STONITH to ensure data integrity"); + } + + return TRUE; } /* remove nodes that are down, stopping */ /* create +ve rsc_to_node constraints between resources and the nodes they are running on */ /* anything else? */ gboolean unpack_status(xmlNode * status, pe_working_set_t *data_set) { - const char *id = NULL; - const char *uname = NULL; + const char *id = NULL; + const char *uname = NULL; - xmlNode * lrm_rsc = NULL; - xmlNode * attrs = NULL; - node_t *this_node = NULL; + xmlNode * lrm_rsc = NULL; + xmlNode * attrs = NULL; + xmlNode * node_state = NULL; + node_t *this_node = NULL; - crm_debug_3("Beginning unpack"); - xml_child_iter_filter( - status, node_state, XML_CIB_TAG_STATE, - - id = crm_element_value(node_state, XML_ATTR_ID); - uname = crm_element_value(node_state, XML_ATTR_UNAME); - attrs = find_xml_node(node_state, XML_TAG_TRANSIENT_NODEATTRS, FALSE); + crm_debug_3("Beginning unpack"); + for(node_state = status; node_state != NULL; node_state = node_state->next) { + if(crm_str_eq((const char *)node_state->name, XML_CIB_TAG_STATE, TRUE)) { + id = crm_element_value(node_state, XML_ATTR_ID); + uname = crm_element_value(node_state, XML_ATTR_UNAME); + attrs = find_xml_node(node_state, XML_TAG_TRANSIENT_NODEATTRS, FALSE); - crm_debug_3("Processing node id=%s, uname=%s", id, uname); - this_node = pe_find_node_id(data_set->nodes, id); + crm_debug_3("Processing node id=%s, uname=%s", id, uname); + this_node = pe_find_node_id(data_set->nodes, id); - if(uname == NULL) { - /* error */ - continue; + if(uname == NULL) { + /* error */ + continue; - } else if(this_node == NULL) { - crm_config_warn("Node %s in status section no longer exists", - uname); - continue; - } + } else if(this_node == NULL) { + crm_config_warn("Node %s in status section no longer exists", + uname); + continue; + } - /* Mark the node as provisionally clean - * - at least we have seen it in the current cluster's lifetime - */ - this_node->details->unclean = FALSE; - add_node_attrs(attrs, this_node, TRUE, data_set); + /* Mark the node as provisionally clean + * - at least we have seen it in the current cluster's lifetime + */ + this_node->details->unclean = FALSE; + add_node_attrs(attrs, this_node, TRUE, data_set); - if(crm_is_true(g_hash_table_lookup(this_node->details->attrs, "standby"))) { - crm_info("Node %s is in standby-mode", - this_node->details->uname); - this_node->details->standby = TRUE; - } + if(crm_is_true(g_hash_table_lookup(this_node->details->attrs, "standby"))) { + crm_info("Node %s is in standby-mode", + this_node->details->uname); + this_node->details->standby = TRUE; + } - crm_debug_3("determining node state"); - determine_online_status(node_state, this_node, data_set); - - if(this_node->details->online - && data_set->no_quorum_policy == no_quorum_suicide) { - /* Everything else should flow from this automatically - * At least until the PE becomes able to migrate off healthy resources - */ - pe_fence_node(data_set, this_node, "because the cluster does not have quorum"); - } - ); + crm_debug_3("determining node state"); + determine_online_status(node_state, this_node, data_set); + + if(this_node->details->online + && data_set->no_quorum_policy == no_quorum_suicide) { + /* Everything else should flow from this automatically + * At least until the PE becomes able to migrate off healthy resources + */ + pe_fence_node(data_set, this_node, "because the cluster does not have quorum"); + } + } + } - /* Split into two phases so that we know all node states before we hit migration ops */ + /* Split into two phases so that we know all node states before we hit migration ops */ + for(node_state = status; node_state != NULL; node_state = node_state->next) { + if(crm_str_eq((const char *)node_state->name, XML_CIB_TAG_STATE, TRUE)) { - xml_child_iter_filter( - status, node_state, XML_CIB_TAG_STATE, - id = crm_element_value(node_state, XML_ATTR_ID); - lrm_rsc = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE); - lrm_rsc = find_xml_node(lrm_rsc, XML_LRM_TAG_RESOURCES, FALSE); + id = crm_element_value(node_state, XML_ATTR_ID); + lrm_rsc = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE); + lrm_rsc = find_xml_node(lrm_rsc, XML_LRM_TAG_RESOURCES, FALSE); - crm_debug_3("Processing lrm operations on node %s", id); - this_node = pe_find_node_id(data_set->nodes, id); + crm_debug_3("Processing lrm operations on node %s", id); + this_node = pe_find_node_id(data_set->nodes, id); - if(this_node->details->online || is_set(data_set->flags, pe_flag_stonith_enabled)) { - /* offline nodes run no resources... - * unless stonith is enabled in which case we need to - * make sure rsc start events happen after the stonith - */ - crm_debug_3("Processing lrm resource entries"); - unpack_lrm_resources(this_node, lrm_rsc, data_set); - } - ); - - return TRUE; + if(this_node->details->online || is_set(data_set->flags, pe_flag_stonith_enabled)) { + /* offline nodes run no resources... + * unless stonith is enabled in which case we need to + * make sure rsc start events happen after the stonith + */ + crm_debug_3("Processing lrm resource entries"); + unpack_lrm_resources(this_node, lrm_rsc, data_set); + } + } + } + return TRUE; } static gboolean determine_online_status_no_fencing(pe_working_set_t *data_set, xmlNode * node_state, node_t *this_node) { - gboolean online = FALSE; - const char *join_state = crm_element_value(node_state, XML_CIB_ATTR_JOINSTATE); - const char *crm_state = crm_element_value(node_state, XML_CIB_ATTR_CRMDSTATE); - const char *ccm_state = crm_element_value(node_state, XML_CIB_ATTR_INCCM); - const char *ha_state = crm_element_value(node_state, XML_CIB_ATTR_HASTATE); - const char *exp_state = crm_element_value(node_state, XML_CIB_ATTR_EXPSTATE); - - if(ha_state == NULL) { - ha_state = DEADSTATUS; - } + gboolean online = FALSE; + const char *join_state = crm_element_value(node_state, XML_CIB_ATTR_JOINSTATE); + const char *crm_state = crm_element_value(node_state, XML_CIB_ATTR_CRMDSTATE); + const char *ccm_state = crm_element_value(node_state, XML_CIB_ATTR_INCCM); + const char *ha_state = crm_element_value(node_state, XML_CIB_ATTR_HASTATE); + const char *exp_state = crm_element_value(node_state, XML_CIB_ATTR_EXPSTATE); + + if(ha_state == NULL) { + ha_state = DEADSTATUS; + } - if(!crm_is_true(ccm_state) || safe_str_eq(ha_state, DEADSTATUS)){ - crm_debug_2("Node is down: ha_state=%s, ccm_state=%s", - crm_str(ha_state), crm_str(ccm_state)); - - } else if(safe_str_eq(crm_state, ONLINESTATUS)) { - if(safe_str_eq(join_state, CRMD_JOINSTATE_MEMBER)) { - online = TRUE; - } else { - crm_debug("Node is not ready to run resources: %s", join_state); - } - - } else if(this_node->details->expected_up == FALSE) { - crm_debug_2("CRMd is down: ha_state=%s, ccm_state=%s", - crm_str(ha_state), crm_str(ccm_state)); - crm_debug_2("\tcrm_state=%s, join_state=%s, expected=%s", - crm_str(crm_state), crm_str(join_state), - crm_str(exp_state)); + if(!crm_is_true(ccm_state) || safe_str_eq(ha_state, DEADSTATUS)){ + crm_debug_2("Node is down: ha_state=%s, ccm_state=%s", + crm_str(ha_state), crm_str(ccm_state)); + } else if(safe_str_eq(crm_state, ONLINESTATUS)) { + if(safe_str_eq(join_state, CRMD_JOINSTATE_MEMBER)) { + online = TRUE; } else { - /* mark it unclean */ - pe_fence_node(data_set, this_node, "because it is partially and/or un-expectedly down"); - crm_info("\tha_state=%s, ccm_state=%s," - " crm_state=%s, join_state=%s, expected=%s", - crm_str(ha_state), crm_str(ccm_state), - crm_str(crm_state), crm_str(join_state), - crm_str(exp_state)); + crm_debug("Node is not ready to run resources: %s", join_state); } - return online; + + } else if(this_node->details->expected_up == FALSE) { + crm_debug_2("CRMd is down: ha_state=%s, ccm_state=%s", + crm_str(ha_state), crm_str(ccm_state)); + crm_debug_2("\tcrm_state=%s, join_state=%s, expected=%s", + crm_str(crm_state), crm_str(join_state), + crm_str(exp_state)); + + } else { + /* mark it unclean */ + pe_fence_node(data_set, this_node, "because it is partially and/or un-expectedly down"); + crm_info("\tha_state=%s, ccm_state=%s," + " crm_state=%s, join_state=%s, expected=%s", + crm_str(ha_state), crm_str(ccm_state), + crm_str(crm_state), crm_str(join_state), + crm_str(exp_state)); + } + return online; } static gboolean determine_online_status_fencing(pe_working_set_t *data_set, xmlNode * node_state, node_t *this_node) { - gboolean online = FALSE; - gboolean do_terminate = FALSE; - const char *join_state = crm_element_value(node_state, XML_CIB_ATTR_JOINSTATE); - const char *crm_state = crm_element_value(node_state, XML_CIB_ATTR_CRMDSTATE); - const char *ccm_state = crm_element_value(node_state, XML_CIB_ATTR_INCCM); - const char *ha_state = crm_element_value(node_state, XML_CIB_ATTR_HASTATE); - const char *exp_state = crm_element_value(node_state, XML_CIB_ATTR_EXPSTATE); - const char *terminate = g_hash_table_lookup(this_node->details->attrs, "terminate"); - - if(ha_state == NULL) { - ha_state = DEADSTATUS; - } + gboolean online = FALSE; + gboolean do_terminate = FALSE; + const char *join_state = crm_element_value(node_state, XML_CIB_ATTR_JOINSTATE); + const char *crm_state = crm_element_value(node_state, XML_CIB_ATTR_CRMDSTATE); + const char *ccm_state = crm_element_value(node_state, XML_CIB_ATTR_INCCM); + const char *ha_state = crm_element_value(node_state, XML_CIB_ATTR_HASTATE); + const char *exp_state = crm_element_value(node_state, XML_CIB_ATTR_EXPSTATE); + const char *terminate = g_hash_table_lookup(this_node->details->attrs, "terminate"); + + if(ha_state == NULL) { + ha_state = DEADSTATUS; + } - if(crm_is_true(terminate)) { - do_terminate = TRUE; + if(crm_is_true(terminate)) { + do_terminate = TRUE; - } else if(terminate != NULL && strlen(terminate) > 0) { - /* could be a time() value */ - char t = terminate[0]; - if(t != '0' && isdigit(t)) { - do_terminate = TRUE; - } + } else if(terminate != NULL && strlen(terminate) > 0) { + /* could be a time() value */ + char t = terminate[0]; + if(t != '0' && isdigit(t)) { + do_terminate = TRUE; } + } - if(crm_is_true(ccm_state) - && safe_str_eq(ha_state, ACTIVESTATUS) - && safe_str_eq(crm_state, ONLINESTATUS)) { - - if(safe_str_eq(join_state, CRMD_JOINSTATE_MEMBER)) { - online = TRUE; - if(do_terminate) { - pe_fence_node(data_set, this_node, "because termination was requested"); - } + if(crm_is_true(ccm_state) + && safe_str_eq(ha_state, ACTIVESTATUS) + && safe_str_eq(crm_state, ONLINESTATUS)) { + + if(safe_str_eq(join_state, CRMD_JOINSTATE_MEMBER)) { + online = TRUE; + if(do_terminate) { + pe_fence_node(data_set, this_node, "because termination was requested"); + } - } else if(join_state == exp_state /* == NULL */) { - crm_info("Node %s is coming up", this_node->details->uname); - crm_debug("\tha_state=%s, ccm_state=%s," - " crm_state=%s, join_state=%s, expected=%s", - crm_str(ha_state), crm_str(ccm_state), - crm_str(crm_state), crm_str(join_state), - crm_str(exp_state)); + } else if(join_state == exp_state /* == NULL */) { + crm_info("Node %s is coming up", this_node->details->uname); + crm_debug("\tha_state=%s, ccm_state=%s," + " crm_state=%s, join_state=%s, expected=%s", + crm_str(ha_state), crm_str(ccm_state), + crm_str(crm_state), crm_str(join_state), + crm_str(exp_state)); - } else if(safe_str_eq(join_state, CRMD_JOINSTATE_PENDING)) { - crm_info("Node %s is not ready to run resources", - this_node->details->uname); - this_node->details->standby = TRUE; - this_node->details->pending = TRUE; - online = TRUE; + } else if(safe_str_eq(join_state, CRMD_JOINSTATE_PENDING)) { + crm_info("Node %s is not ready to run resources", + this_node->details->uname); + this_node->details->standby = TRUE; + this_node->details->pending = TRUE; + online = TRUE; - } else if(safe_str_eq(join_state, CRMD_JOINSTATE_NACK)) { - crm_warn("Node %s is not part of the cluster", - this_node->details->uname); - this_node->details->standby = TRUE; - this_node->details->pending = TRUE; - online = TRUE; + } else if(safe_str_eq(join_state, CRMD_JOINSTATE_NACK)) { + crm_warn("Node %s is not part of the cluster", + this_node->details->uname); + this_node->details->standby = TRUE; + this_node->details->pending = TRUE; + online = TRUE; - } else if(safe_str_eq(join_state, exp_state)) { - crm_info("Node %s is still coming up: %s", - this_node->details->uname, join_state); - crm_info("\tha_state=%s, ccm_state=%s, crm_state=%s", - crm_str(ha_state), crm_str(ccm_state), crm_str(crm_state)); - this_node->details->standby = TRUE; - this_node->details->pending = TRUE; - online = TRUE; + } else if(safe_str_eq(join_state, exp_state)) { + crm_info("Node %s is still coming up: %s", + this_node->details->uname, join_state); + crm_info("\tha_state=%s, ccm_state=%s, crm_state=%s", + crm_str(ha_state), crm_str(ccm_state), crm_str(crm_state)); + this_node->details->standby = TRUE; + this_node->details->pending = TRUE; + online = TRUE; - } else { - crm_warn("Node %s (%s) is un-expectedly down", - this_node->details->uname, this_node->details->id); - crm_info("\tha_state=%s, ccm_state=%s," - " crm_state=%s, join_state=%s, expected=%s", - crm_str(ha_state), crm_str(ccm_state), - crm_str(crm_state), crm_str(join_state), - crm_str(exp_state)); - pe_fence_node(data_set, this_node, "because it is un-expectedly down"); - } + } else { + crm_warn("Node %s (%s) is un-expectedly down", + this_node->details->uname, this_node->details->id); + crm_info("\tha_state=%s, ccm_state=%s," + " crm_state=%s, join_state=%s, expected=%s", + crm_str(ha_state), crm_str(ccm_state), + crm_str(crm_state), crm_str(join_state), + crm_str(exp_state)); + pe_fence_node(data_set, this_node, "because it is un-expectedly down"); + } - } else if(crm_is_true(ccm_state) == FALSE - && safe_str_eq(ha_state, DEADSTATUS) - && safe_str_eq(crm_state, OFFLINESTATUS) - && this_node->details->expected_up == FALSE) { - crm_debug("Node %s is down: join_state=%s, expected=%s", - this_node->details->uname, - crm_str(join_state), crm_str(exp_state)); + } else if(crm_is_true(ccm_state) == FALSE + && safe_str_eq(ha_state, DEADSTATUS) + && safe_str_eq(crm_state, OFFLINESTATUS) + && this_node->details->expected_up == FALSE) { + crm_debug("Node %s is down: join_state=%s, expected=%s", + this_node->details->uname, + crm_str(join_state), crm_str(exp_state)); #if 0 - /* While a nice optimization, it causes the cluster to block until the node - * comes back online. Which is a serious problem if the cluster software - * is not configured to start at boot or stonith is configured to merely - * stop the node instead of restart it. - * Easily triggered by setting terminate=true for the DC - */ - } else if(do_terminate) { - crm_info("Node %s is %s after forced termination", - this_node->details->uname, crm_is_true(ccm_state)?"coming up":"going down"); - crm_debug("\tha_state=%s, ccm_state=%s," - " crm_state=%s, join_state=%s, expected=%s", - crm_str(ha_state), crm_str(ccm_state), - crm_str(crm_state), crm_str(join_state), - crm_str(exp_state)); + /* While a nice optimization, it causes the cluster to block until the node + * comes back online. Which is a serious problem if the cluster software + * is not configured to start at boot or stonith is configured to merely + * stop the node instead of restart it. + * Easily triggered by setting terminate=true for the DC + */ + } else if(do_terminate) { + crm_info("Node %s is %s after forced termination", + this_node->details->uname, crm_is_true(ccm_state)?"coming up":"going down"); + crm_debug("\tha_state=%s, ccm_state=%s," + " crm_state=%s, join_state=%s, expected=%s", + crm_str(ha_state), crm_str(ccm_state), + crm_str(crm_state), crm_str(join_state), + crm_str(exp_state)); - if(crm_is_true(ccm_state) == FALSE) { - this_node->details->standby = TRUE; - this_node->details->pending = TRUE; - online = TRUE; - } + if(crm_is_true(ccm_state) == FALSE) { + this_node->details->standby = TRUE; + this_node->details->pending = TRUE; + online = TRUE; + } #endif - } else if(this_node->details->expected_up) { - /* mark it unclean */ - pe_fence_node(data_set, this_node, "because it is un-expectedly down"); - crm_info("\tha_state=%s, ccm_state=%s," - " crm_state=%s, join_state=%s, expected=%s", - crm_str(ha_state), crm_str(ccm_state), - crm_str(crm_state), crm_str(join_state), - crm_str(exp_state)); + } else if(this_node->details->expected_up) { + /* mark it unclean */ + pe_fence_node(data_set, this_node, "because it is un-expectedly down"); + crm_info("\tha_state=%s, ccm_state=%s," + " crm_state=%s, join_state=%s, expected=%s", + crm_str(ha_state), crm_str(ccm_state), + crm_str(crm_state), crm_str(join_state), + crm_str(exp_state)); - } else { - crm_info("Node %s is down", this_node->details->uname); - crm_debug("\tha_state=%s, ccm_state=%s," - " crm_state=%s, join_state=%s, expected=%s", - crm_str(ha_state), crm_str(ccm_state), - crm_str(crm_state), crm_str(join_state), - crm_str(exp_state)); - } - return online; + } else { + crm_info("Node %s is down", this_node->details->uname); + crm_debug("\tha_state=%s, ccm_state=%s," + " crm_state=%s, join_state=%s, expected=%s", + crm_str(ha_state), crm_str(ccm_state), + crm_str(crm_state), crm_str(join_state), + crm_str(exp_state)); + } + return online; } gboolean determine_online_status( - xmlNode * node_state, node_t *this_node, pe_working_set_t *data_set) + xmlNode * node_state, node_t *this_node, pe_working_set_t *data_set) { - gboolean online = FALSE; - const char *shutdown = NULL; - const char *exp_state = crm_element_value(node_state, XML_CIB_ATTR_EXPSTATE); + gboolean online = FALSE; + const char *shutdown = NULL; + const char *exp_state = crm_element_value(node_state, XML_CIB_ATTR_EXPSTATE); - if(this_node == NULL) { - crm_config_err("No node to check"); - return online; - } + if(this_node == NULL) { + crm_config_err("No node to check"); + return online; + } - this_node->details->shutdown = FALSE; - this_node->details->expected_up = FALSE; - shutdown = g_hash_table_lookup(this_node->details->attrs, XML_CIB_ATTR_SHUTDOWN); + this_node->details->shutdown = FALSE; + this_node->details->expected_up = FALSE; + shutdown = g_hash_table_lookup(this_node->details->attrs, XML_CIB_ATTR_SHUTDOWN); - if(shutdown != NULL && safe_str_neq("0", shutdown)) { - this_node->details->shutdown = TRUE; + if(shutdown != NULL && safe_str_neq("0", shutdown)) { + this_node->details->shutdown = TRUE; - } else if(safe_str_eq(exp_state, CRMD_JOINSTATE_MEMBER)) { - this_node->details->expected_up = TRUE; - } + } else if(safe_str_eq(exp_state, CRMD_JOINSTATE_MEMBER)) { + this_node->details->expected_up = TRUE; + } - if(is_set(data_set->flags, pe_flag_stonith_enabled) == FALSE) { - online = determine_online_status_no_fencing( - data_set, node_state, this_node); + if(is_set(data_set->flags, pe_flag_stonith_enabled) == FALSE) { + online = determine_online_status_no_fencing( + data_set, node_state, this_node); - } else { - online = determine_online_status_fencing( - data_set, node_state, this_node); - } + } else { + online = determine_online_status_fencing( + data_set, node_state, this_node); + } - if(online) { - this_node->details->online = TRUE; + if(online) { + this_node->details->online = TRUE; - } else { - /* remove node from contention */ - this_node->fixed = TRUE; - this_node->weight = -INFINITY; - } + } else { + /* remove node from contention */ + this_node->fixed = TRUE; + this_node->weight = -INFINITY; + } - if(online && this_node->details->shutdown) { - /* dont run resources here */ - this_node->fixed = TRUE; - this_node->weight = -INFINITY; - } + if(online && this_node->details->shutdown) { + /* dont run resources here */ + this_node->fixed = TRUE; + this_node->weight = -INFINITY; + } - if(this_node->details->unclean) { - pe_proc_warn("Node %s is unclean", this_node->details->uname); + if(this_node->details->unclean) { + pe_proc_warn("Node %s is unclean", this_node->details->uname); - } else if(this_node->details->online) { - crm_info("Node %s is %s", this_node->details->uname, - this_node->details->shutdown?"shutting down": - this_node->details->pending?"pending": - this_node->details->standby?"standby":"online"); + } else if(this_node->details->online) { + crm_info("Node %s is %s", this_node->details->uname, + this_node->details->shutdown?"shutting down": + this_node->details->pending?"pending": + this_node->details->standby?"standby":"online"); - } else { - crm_debug_2("Node %s is offline", this_node->details->uname); - } + } else { + crm_debug_2("Node %s is offline", this_node->details->uname); + } - return online; + return online; } #define set_char(x) last_rsc_id[lpc] = x; complete = TRUE; char * clone_zero(const char *last_rsc_id) { int lpc = 0; char *zero = NULL; CRM_CHECK(last_rsc_id != NULL, return NULL); if(last_rsc_id != NULL) { lpc = strlen(last_rsc_id); } while(--lpc > 0) { switch(last_rsc_id[lpc]) { case 0: return NULL; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': break; case ':': crm_malloc0(zero, lpc + 3); memcpy(zero, last_rsc_id, lpc); zero[lpc] = ':'; zero[lpc+1] = '0'; zero[lpc+2] = 0; return zero; } } return NULL; } char * increment_clone(char *last_rsc_id) { - int lpc = 0; - int len = 0; - char *tmp = NULL; - gboolean complete = FALSE; - - CRM_CHECK(last_rsc_id != NULL, return NULL); - if(last_rsc_id != NULL) { - len = strlen(last_rsc_id); - } + int lpc = 0; + int len = 0; + char *tmp = NULL; + gboolean complete = FALSE; + + CRM_CHECK(last_rsc_id != NULL, return NULL); + if(last_rsc_id != NULL) { + len = strlen(last_rsc_id); + } - lpc = len-1; - while(complete == FALSE && lpc > 0) { - switch (last_rsc_id[lpc]) { - case 0: - lpc--; - break; - case '0': - set_char('1'); - break; - case '1': - set_char('2'); - break; - case '2': - set_char('3'); - break; - case '3': - set_char('4'); - break; - case '4': - set_char('5'); - break; - case '5': - set_char('6'); - break; - case '6': - set_char('7'); - break; - case '7': - set_char('8'); - break; - case '8': - set_char('9'); - break; - case '9': - last_rsc_id[lpc] = '0'; - lpc--; - break; - case ':': - tmp = last_rsc_id; - crm_malloc0(last_rsc_id, len + 2); - memcpy(last_rsc_id, tmp, len); - last_rsc_id[++lpc] = '1'; - last_rsc_id[len] = '0'; - last_rsc_id[len+1] = 0; - complete = TRUE; - crm_free(tmp); - break; - default: - crm_err("Unexpected char: %c (%d)", - last_rsc_id[lpc], lpc); - break; - } + lpc = len-1; + while(complete == FALSE && lpc > 0) { + switch (last_rsc_id[lpc]) { + case 0: + lpc--; + break; + case '0': + set_char('1'); + break; + case '1': + set_char('2'); + break; + case '2': + set_char('3'); + break; + case '3': + set_char('4'); + break; + case '4': + set_char('5'); + break; + case '5': + set_char('6'); + break; + case '6': + set_char('7'); + break; + case '7': + set_char('8'); + break; + case '8': + set_char('9'); + break; + case '9': + last_rsc_id[lpc] = '0'; + lpc--; + break; + case ':': + tmp = last_rsc_id; + crm_malloc0(last_rsc_id, len + 2); + memcpy(last_rsc_id, tmp, len); + last_rsc_id[++lpc] = '1'; + last_rsc_id[len] = '0'; + last_rsc_id[len+1] = 0; + complete = TRUE; + crm_free(tmp); + break; + default: + crm_err("Unexpected char: %c (%d)", + last_rsc_id[lpc], lpc); + break; } - return last_rsc_id; + } + return last_rsc_id; } static resource_t * create_fake_resource(const char *rsc_id, xmlNode *rsc_entry, pe_working_set_t *data_set) { - resource_t *rsc = NULL; - xmlNode *xml_rsc = create_xml_node(NULL, XML_CIB_TAG_RESOURCE); - copy_in_properties(xml_rsc, rsc_entry); - crm_xml_add(xml_rsc, XML_ATTR_ID, rsc_id); - crm_log_xml_debug(xml_rsc, "Orphan resource"); + resource_t *rsc = NULL; + xmlNode *xml_rsc = create_xml_node(NULL, XML_CIB_TAG_RESOURCE); + copy_in_properties(xml_rsc, rsc_entry); + crm_xml_add(xml_rsc, XML_ATTR_ID, rsc_id); + crm_log_xml_debug(xml_rsc, "Orphan resource"); - if(!common_unpack(xml_rsc, &rsc, NULL, data_set)) { - return NULL; - } + if(!common_unpack(xml_rsc, &rsc, NULL, data_set)) { + return NULL; + } - set_bit(rsc->flags, pe_rsc_orphan); - data_set->resources = g_list_append(data_set->resources, rsc); - return rsc; + set_bit(rsc->flags, pe_rsc_orphan); + data_set->resources = g_list_append(data_set->resources, rsc); + return rsc; } extern resource_t *create_child_clone(resource_t *rsc, int sub_id, pe_working_set_t *data_set); static resource_t *find_clone(pe_working_set_t *data_set, node_t *node, resource_t *parent, const char *rsc_id) { int len = 0; resource_t *rsc = NULL; char *base = clone_zero(rsc_id); char *alt_rsc_id = crm_strdup(rsc_id); CRM_ASSERT(parent != NULL); CRM_ASSERT(parent->variant == pe_clone || parent->variant == pe_master); if(base) { len = strlen(base); } if(len > 0) { base[len-1] = 0; } crm_debug_3("Looking for %s on %s in %s %d", rsc_id, node->details->uname, parent->id, is_set(parent->flags, pe_rsc_unique)); if(is_set(parent->flags, pe_rsc_unique)) { crm_debug_3("Looking for %s", rsc_id); rsc = parent->fns->find_rsc(parent, rsc_id, FALSE, FALSE, NULL, TRUE); } else { rsc = parent->fns->find_rsc(parent, base, FALSE, TRUE, node, TRUE); if(rsc != NULL && rsc->running_on) { GListPtr gIter = parent->children; rsc = NULL; crm_debug_3("Looking for an existing orphan for %s: %s on %s", parent->id, rsc_id, node->details->uname); /* There is already an instance of this _anonymous_ clone active on "node". * * If there is a partially active orphan (only applies to clone groups) on * the same node, use that. * Otherwise create a new (orphaned) instance at "orphan_check:". - */ + */ for(; gIter != NULL; gIter = gIter->next) { resource_t *child = (resource_t*)gIter->data; node_t *loc = child->fns->location(child, NULL, TRUE); if(loc && loc->details == node->details) { resource_t *tmp = child->fns->find_rsc(child, base, FALSE, TRUE, NULL, TRUE); if(tmp && tmp->running_on == NULL) { rsc = tmp; break; } } } goto orphan_check; } while(rsc == NULL) { crm_debug_3("Trying %s", alt_rsc_id); rsc = parent->fns->find_rsc(parent, alt_rsc_id, FALSE, FALSE, NULL, TRUE); if(rsc == NULL) { break; } else if(rsc->running_on == NULL) { break; } alt_rsc_id = increment_clone(alt_rsc_id); rsc = NULL; } } orphan_check: if(rsc == NULL) { /* Create an extra orphan */ resource_t *top = create_child_clone(parent, -1, data_set); crm_debug("Created orphan for %s: %s on %s", parent->id, rsc_id, node->details->uname); rsc = top->fns->find_rsc(top, base, FALSE, TRUE, NULL, TRUE); CRM_ASSERT(rsc != NULL); } crm_free(rsc->clone_name); rsc->clone_name = NULL; if(safe_str_neq(rsc_id, rsc->id)) { crm_info("Internally renamed %s on %s to %s%s", rsc_id, node->details->uname, rsc->id, is_set(rsc->flags, pe_rsc_orphan)?" (ORPHAN)":""); rsc->clone_name = crm_strdup(rsc_id); } crm_free(alt_rsc_id); crm_free(base); return rsc; } static resource_t * unpack_find_resource( - pe_working_set_t *data_set, node_t *node, const char *rsc_id, xmlNode *rsc_entry) + pe_working_set_t *data_set, node_t *node, const char *rsc_id, xmlNode *rsc_entry) { - resource_t *rsc = NULL; - resource_t *clone_parent = NULL; - char *alt_rsc_id = crm_strdup(rsc_id); + resource_t *rsc = NULL; + resource_t *clone_parent = NULL; + char *alt_rsc_id = crm_strdup(rsc_id); - crm_debug_2("looking for %s", rsc_id); + crm_debug_2("looking for %s", rsc_id); - rsc = pe_find_resource(data_set->resources, alt_rsc_id); - /* no match */ - if(rsc == NULL) { - /* Even when clone-max=0, we still create a single :0 orphan to match against */ - char *tmp = clone_zero(alt_rsc_id); - resource_t *clone0 = pe_find_resource(data_set->resources, tmp); - clone_parent = uber_parent(clone0); - crm_free(tmp); + rsc = pe_find_resource(data_set->resources, alt_rsc_id); + /* no match */ + if(rsc == NULL) { + /* Even when clone-max=0, we still create a single :0 orphan to match against */ + char *tmp = clone_zero(alt_rsc_id); + resource_t *clone0 = pe_find_resource(data_set->resources, tmp); + clone_parent = uber_parent(clone0); + crm_free(tmp); - crm_debug_2("%s not found: %s", alt_rsc_id, clone_parent?clone_parent->id:"orphan"); + crm_debug_2("%s not found: %s", alt_rsc_id, clone_parent?clone_parent->id:"orphan"); - } else { - clone_parent = uber_parent(rsc); - } + } else { + clone_parent = uber_parent(rsc); + } - if(clone_parent && clone_parent->variant > pe_group) { - rsc = find_clone(data_set, node, clone_parent, rsc_id); - CRM_ASSERT(rsc != NULL); - } + if(clone_parent && clone_parent->variant > pe_group) { + rsc = find_clone(data_set, node, clone_parent, rsc_id); + CRM_ASSERT(rsc != NULL); + } - crm_free(alt_rsc_id); - return rsc; + crm_free(alt_rsc_id); + return rsc; } static resource_t * process_orphan_resource(xmlNode *rsc_entry, node_t *node, pe_working_set_t *data_set) { - resource_t *rsc = NULL; - const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID); + resource_t *rsc = NULL; + const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID); - crm_debug("Detected orphan resource %s on %s", rsc_id, node->details->uname); - rsc = create_fake_resource(rsc_id, rsc_entry, data_set); + crm_debug("Detected orphan resource %s on %s", rsc_id, node->details->uname); + rsc = create_fake_resource(rsc_id, rsc_entry, data_set); - if(is_set(data_set->flags, pe_flag_stop_rsc_orphans) == FALSE) { - clear_bit(rsc->flags, pe_rsc_managed); + if(is_set(data_set->flags, pe_flag_stop_rsc_orphans) == FALSE) { + clear_bit(rsc->flags, pe_rsc_managed); - } else { - print_resource(LOG_DEBUG_3, "Added orphan", rsc, FALSE); + } else { + print_resource(LOG_DEBUG_3, "Added orphan", rsc, FALSE); - CRM_CHECK(rsc != NULL, return NULL); - resource_location(rsc, NULL, -INFINITY, "__orphan_dont_run__", data_set); - } - return rsc; + CRM_CHECK(rsc != NULL, return NULL); + resource_location(rsc, NULL, -INFINITY, "__orphan_dont_run__", data_set); + } + return rsc; } static void process_rsc_state(resource_t *rsc, node_t *node, enum action_fail_response on_fail, xmlNode *migrate_op, pe_working_set_t *data_set) { - crm_debug_2("Resource %s is %s on %s: on_fail=%s", - rsc->id, role2text(rsc->role), + crm_debug_2("Resource %s is %s on %s: on_fail=%s", + rsc->id, role2text(rsc->role), node->details->uname, fail2text(on_fail)); - /* process current state */ - if(rsc->role != RSC_ROLE_UNKNOWN) { - resource_t *iter = rsc; - while(iter) { - if(g_hash_table_lookup(iter->known_on, node->details->id) == NULL) { - node_t *n = node_copy(node); - g_hash_table_insert(iter->known_on, (gpointer)n->details->id, n); - } - if(is_set(iter->flags, pe_rsc_unique)) { - break; - } - iter = iter->parent; + /* process current state */ + if(rsc->role != RSC_ROLE_UNKNOWN) { + resource_t *iter = rsc; + while(iter) { + if(g_hash_table_lookup(iter->known_on, node->details->id) == NULL) { + node_t *n = node_copy(node); + g_hash_table_insert(iter->known_on, (gpointer)n->details->id, n); + } + if(is_set(iter->flags, pe_rsc_unique)) { + break; } + iter = iter->parent; } + } - if(node->details->unclean) { - /* No extra processing needed - * Also allows resources to be started again after a node is shot - */ - on_fail = action_fail_ignore; - } + if(node->details->unclean) { + /* No extra processing needed + * Also allows resources to be started again after a node is shot + */ + on_fail = action_fail_ignore; + } - switch(on_fail) { - case action_fail_ignore: - /* nothing to do */ - break; + switch(on_fail) { + case action_fail_ignore: + /* nothing to do */ + break; - case action_fail_fence: - /* treat it as if it is still running - * but also mark the node as unclean - */ - pe_fence_node(data_set, node, "to recover from resource failure(s)"); - break; + case action_fail_fence: + /* treat it as if it is still running + * but also mark the node as unclean + */ + pe_fence_node(data_set, node, "to recover from resource failure(s)"); + break; - case action_fail_standby: - node->details->standby = TRUE; - node->details->standby_onfail = TRUE; - break; + case action_fail_standby: + node->details->standby = TRUE; + node->details->standby_onfail = TRUE; + break; - case action_fail_block: - /* is_managed == FALSE will prevent any - * actions being sent for the resource - */ - clear_bit(rsc->flags, pe_rsc_managed); - break; + case action_fail_block: + /* is_managed == FALSE will prevent any + * actions being sent for the resource + */ + clear_bit(rsc->flags, pe_rsc_managed); + break; - case action_fail_migrate: - /* make sure it comes up somewhere else - * or not at all - */ - resource_location(rsc, node, -INFINITY, - "__action_migration_auto__",data_set); - break; + case action_fail_migrate: + /* make sure it comes up somewhere else + * or not at all + */ + resource_location(rsc, node, -INFINITY, + "__action_migration_auto__",data_set); + break; - case action_fail_stop: - rsc->next_role = RSC_ROLE_STOPPED; - break; + case action_fail_stop: + rsc->next_role = RSC_ROLE_STOPPED; + break; - case action_fail_recover: - if(rsc->role != RSC_ROLE_STOPPED && rsc->role != RSC_ROLE_UNKNOWN) { - set_bit(rsc->flags, pe_rsc_failed); - stop_action(rsc, node, FALSE); - } - break; - } + case action_fail_recover: + if(rsc->role != RSC_ROLE_STOPPED && rsc->role != RSC_ROLE_UNKNOWN) { + set_bit(rsc->flags, pe_rsc_failed); + stop_action(rsc, node, FALSE); + } + break; + } - if(rsc->role != RSC_ROLE_STOPPED && rsc->role != RSC_ROLE_UNKNOWN) { - if(is_set(rsc->flags, pe_rsc_orphan)) { - if(is_set(rsc->flags, pe_rsc_managed)) { - crm_config_warn("Detected active orphan %s running on %s", - rsc->id, node->details->uname); - } else { - crm_config_warn("Cluster configured not to stop active orphans." - " %s must be stopped manually on %s", - rsc->id, node->details->uname); - } - } + if(rsc->role != RSC_ROLE_STOPPED && rsc->role != RSC_ROLE_UNKNOWN) { + if(is_set(rsc->flags, pe_rsc_orphan)) { + if(is_set(rsc->flags, pe_rsc_managed)) { + crm_config_warn("Detected active orphan %s running on %s", + rsc->id, node->details->uname); + } else { + crm_config_warn("Cluster configured not to stop active orphans." + " %s must be stopped manually on %s", + rsc->id, node->details->uname); + } + } - native_add_running(rsc, node, data_set); - if(on_fail != action_fail_ignore) { - set_bit(rsc->flags, pe_rsc_failed); - } + native_add_running(rsc, node, data_set); + if(on_fail != action_fail_ignore) { + set_bit(rsc->flags, pe_rsc_failed); + } - } else if(rsc->clone_name) { - crm_debug_2("Resetting clone_name %s for %s (stopped)", - rsc->clone_name, rsc->id); - crm_free(rsc->clone_name); - rsc->clone_name = NULL; + } else if(rsc->clone_name) { + crm_debug_2("Resetting clone_name %s for %s (stopped)", + rsc->clone_name, rsc->id); + crm_free(rsc->clone_name); + rsc->clone_name = NULL; - } else { - char *key = stop_key(rsc); - GListPtr possible_matches = find_actions(rsc->actions, key, node); - GListPtr gIter = possible_matches; - for(; gIter != NULL; gIter = gIter->next) { - action_t *stop = (action_t*)gIter->data; - stop->flags |= pe_action_optional; - } - - crm_free(key); + } else { + char *key = stop_key(rsc); + GListPtr possible_matches = find_actions(rsc->actions, key, node); + GListPtr gIter = possible_matches; + for(; gIter != NULL; gIter = gIter->next) { + action_t *stop = (action_t*)gIter->data; + stop->flags |= pe_action_optional; } + + crm_free(key); + } } /* create active recurring operations as optional */ static void process_recurring(node_t *node, resource_t *rsc, int start_index, int stop_index, GListPtr sorted_op_list, pe_working_set_t *data_set) { int counter = -1; const char *task = NULL; const char *status = NULL; GListPtr gIter = sorted_op_list; crm_debug_3("%s: Start index %d, stop index = %d", rsc->id, start_index, stop_index); for(; gIter != NULL; gIter = gIter->next) { xmlNode *rsc_op = (xmlNode*)gIter->data; int interval = 0; char *key = NULL; const char *id = ID(rsc_op); const char *interval_s = NULL; counter++; if(node->details->online == FALSE) { crm_debug_4("Skipping %s/%s: node is offline", rsc->id, node->details->uname); break; } else if(start_index < stop_index) { crm_debug_4("Skipping %s/%s: not active", rsc->id, node->details->uname); break; } else if(counter <= start_index) { crm_debug_4("Skipping %s/%s: old", id, node->details->uname); continue; } interval_s = crm_element_value(rsc_op,XML_LRM_ATTR_INTERVAL); interval = crm_parse_int(interval_s, "0"); if(interval == 0) { crm_debug_4("Skipping %s/%s: non-recurring", id, node->details->uname); continue; } status = crm_element_value(rsc_op, XML_LRM_ATTR_OPSTATUS); if(safe_str_eq(status, "-1")) { crm_debug_4("Skipping %s/%s: status", id, node->details->uname); continue; } task = crm_element_value(rsc_op, XML_LRM_ATTR_TASK); /* create the action */ key = generate_op_key(rsc->id, task, interval); crm_debug_3("Creating %s/%s", key, node->details->uname); custom_action(rsc, key, task, node, TRUE, TRUE, data_set); } } void calculate_active_ops(GListPtr sorted_op_list, int *start_index, int *stop_index) { int counter = -1; const char *task = NULL; const char *status = NULL; GListPtr gIter = sorted_op_list; *stop_index = -1; *start_index = -1; for(; gIter != NULL; gIter = gIter->next) { xmlNode *rsc_op = (xmlNode*)gIter->data; counter++; task = crm_element_value(rsc_op, XML_LRM_ATTR_TASK); status = crm_element_value(rsc_op, XML_LRM_ATTR_OPSTATUS); if(safe_str_eq(task, CRMD_ACTION_STOP) && safe_str_eq(status, "0")) { *stop_index = counter; } else if(safe_str_eq(task, CRMD_ACTION_START)) { *start_index = counter; } else if(*start_index <= *stop_index && safe_str_eq(task, CRMD_ACTION_STATUS)) { const char *rc = crm_element_value(rsc_op, XML_LRM_ATTR_RC); if(safe_str_eq(rc, "0") || safe_str_eq(rc, "8")) { *start_index = counter; } } } } static void unpack_lrm_rsc_state( - node_t *node, xmlNode * rsc_entry, pe_working_set_t *data_set) + node_t *node, xmlNode * rsc_entry, pe_working_set_t *data_set) { - GListPtr gIter = NULL; - int stop_index = -1; - int start_index = -1; - enum rsc_role_e req_role = RSC_ROLE_UNKNOWN; + GListPtr gIter = NULL; + int stop_index = -1; + int start_index = -1; + enum rsc_role_e req_role = RSC_ROLE_UNKNOWN; - const char *task = NULL; - const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID); + const char *task = NULL; + const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID); - resource_t *rsc = NULL; - GListPtr op_list = NULL; - GListPtr sorted_op_list = NULL; + resource_t *rsc = NULL; + GListPtr op_list = NULL; + GListPtr sorted_op_list = NULL; - xmlNode *migrate_op = NULL; + xmlNode *migrate_op = NULL; + xmlNode *rsc_op = NULL; - enum action_fail_response on_fail = FALSE; - enum rsc_role_e saved_role = RSC_ROLE_UNKNOWN; + enum action_fail_response on_fail = FALSE; + enum rsc_role_e saved_role = RSC_ROLE_UNKNOWN; - crm_debug_3("[%s] Processing %s on %s", - crm_element_name(rsc_entry), rsc_id, node->details->uname); + crm_debug_3("[%s] Processing %s on %s", + crm_element_name(rsc_entry), rsc_id, node->details->uname); - /* extract operations */ - op_list = NULL; - sorted_op_list = NULL; + /* extract operations */ + op_list = NULL; + sorted_op_list = NULL; - xml_child_iter_filter( - rsc_entry, rsc_op, XML_LRM_TAG_RSC_OP, - op_list = g_list_prepend(op_list, rsc_op); - ); - - if(op_list == NULL) { - /* if there are no operations, there is nothing to do */ - return; + for(rsc_op = rsc_entry; rsc_op != NULL; rsc_op = rsc_op->next) { + if(crm_str_eq((const char *)rsc_op->name, XML_LRM_TAG_RSC_OP, TRUE)) { + op_list = g_list_prepend(op_list, rsc_op); } + } - /* find the resource */ - rsc = unpack_find_resource(data_set, node, rsc_id, rsc_entry); - if(rsc == NULL) { - rsc = process_orphan_resource(rsc_entry, node, data_set); - } - CRM_ASSERT(rsc != NULL); - - /* process operations */ - saved_role = rsc->role; - on_fail = action_fail_ignore; - rsc->role = RSC_ROLE_UNKNOWN; - sorted_op_list = g_list_sort(op_list, sort_op_by_callid); + if(op_list == NULL) { + /* if there are no operations, there is nothing to do */ + return; + } + /* find the resource */ + rsc = unpack_find_resource(data_set, node, rsc_id, rsc_entry); + if(rsc == NULL) { + rsc = process_orphan_resource(rsc_entry, node, data_set); + } + CRM_ASSERT(rsc != NULL); - gIter = sorted_op_list; - for(; gIter != NULL; gIter = gIter->next) { - xmlNode *rsc_op = (xmlNode*)gIter->data; - - task = crm_element_value(rsc_op, XML_LRM_ATTR_TASK); - if(safe_str_eq(task, CRMD_ACTION_MIGRATED)) { - migrate_op = rsc_op; - } - - unpack_rsc_op(rsc, node, rsc_op, gIter->next, &on_fail, data_set); + /* process operations */ + saved_role = rsc->role; + on_fail = action_fail_ignore; + rsc->role = RSC_ROLE_UNKNOWN; + sorted_op_list = g_list_sort(op_list, sort_op_by_callid); + + for(gIter = sorted_op_list; gIter != NULL; gIter = gIter->next) { + xmlNode *rsc_op = (xmlNode*)gIter->data; + task = crm_element_value(rsc_op, XML_LRM_ATTR_TASK); + if(safe_str_eq(task, CRMD_ACTION_MIGRATED)) { + migrate_op = rsc_op; } + + unpack_rsc_op(rsc, node, rsc_op, gIter->next, &on_fail, data_set); + } - /* create active recurring operations as optional */ - calculate_active_ops(sorted_op_list, &start_index, &stop_index); - process_recurring(node, rsc, start_index, stop_index, - sorted_op_list, data_set); + /* create active recurring operations as optional */ + calculate_active_ops(sorted_op_list, &start_index, &stop_index); + process_recurring(node, rsc, start_index, stop_index, + sorted_op_list, data_set); - /* no need to free the contents */ - g_list_free(sorted_op_list); + /* no need to free the contents */ + g_list_free(sorted_op_list); - process_rsc_state(rsc, node, on_fail, migrate_op, data_set); - - if(get_target_role(rsc, &req_role)) { - if(rsc->next_role == RSC_ROLE_UNKNOWN || req_role < rsc->next_role) { - crm_debug("%s: Overwriting calculated next role %s" - " with requested next role %s", - rsc->id, role2text(rsc->next_role), - role2text(req_role)); - rsc->next_role = req_role; - - } else if(req_role > rsc->next_role) { - crm_info("%s: Not overwriting calculated next role %s" - " with requested next role %s", - rsc->id, role2text(rsc->next_role), - role2text(req_role)); - } + process_rsc_state(rsc, node, on_fail, migrate_op, data_set); + + if(get_target_role(rsc, &req_role)) { + if(rsc->next_role == RSC_ROLE_UNKNOWN || req_role < rsc->next_role) { + crm_debug("%s: Overwriting calculated next role %s" + " with requested next role %s", + rsc->id, role2text(rsc->next_role), + role2text(req_role)); + rsc->next_role = req_role; + + } else if(req_role > rsc->next_role) { + crm_info("%s: Not overwriting calculated next role %s" + " with requested next role %s", + rsc->id, role2text(rsc->next_role), + role2text(req_role)); } + } - if(saved_role > rsc->role) { - rsc->role = saved_role; - } + if(saved_role > rsc->role) { + rsc->role = saved_role; + } } gboolean unpack_lrm_resources(node_t *node, xmlNode * lrm_rsc_list, pe_working_set_t *data_set) { - CRM_CHECK(node != NULL, return FALSE); + xmlNode *rsc_entry = NULL; + CRM_CHECK(node != NULL, return FALSE); - crm_debug_3("Unpacking resources on %s", node->details->uname); + crm_debug_3("Unpacking resources on %s", node->details->uname); - xml_child_iter_filter( - lrm_rsc_list, rsc_entry, XML_LRM_TAG_RESOURCE, - unpack_lrm_rsc_state(node, rsc_entry, data_set); - ); + for(rsc_entry = lrm_rsc_list; rsc_entry != NULL; rsc_entry = rsc_entry->next) { + if(crm_str_eq((const char *)rsc_entry->name, XML_LRM_TAG_RESOURCE, TRUE)) { + unpack_lrm_rsc_state(node, rsc_entry, data_set); + } + } - return TRUE; + return TRUE; } static void set_active(resource_t *rsc) { resource_t *top = uber_parent(rsc); if(top && top->variant == pe_master) { rsc->role = RSC_ROLE_SLAVE; } else { rsc->role = RSC_ROLE_STARTED; } } static void set_node_score(gpointer key, gpointer value, gpointer user_data) { node_t *node = value; int *score = user_data; node->weight = *score; } #define STATUS_PATH_MAX 1024 static xmlNode *find_lrm_op(const char *resource, const char *op, const char *node, const char *source, pe_working_set_t *data_set) { int offset = 0; char xpath[STATUS_PATH_MAX]; offset += snprintf(xpath+offset, STATUS_PATH_MAX-offset, "//node_state[@id='%s']", node); offset += snprintf(xpath+offset, STATUS_PATH_MAX-offset, "//"XML_LRM_TAG_RESOURCE"[@id='%s']", resource); /* Need to check against transition_magic too? */ if(source && safe_str_eq(op, CRMD_ACTION_MIGRATE)) { offset += snprintf(xpath+offset, STATUS_PATH_MAX-offset, "/"XML_LRM_TAG_RSC_OP"[@operation='%s' and @migrate_target='%s']", op, source); } else if(source && safe_str_eq(op, CRMD_ACTION_MIGRATED)) { offset += snprintf(xpath+offset, STATUS_PATH_MAX-offset, "/"XML_LRM_TAG_RSC_OP"[@operation='%s' and @migrate_source='%s']", op, source); } else { offset += snprintf(xpath+offset, STATUS_PATH_MAX-offset, "/"XML_LRM_TAG_RSC_OP"[@operation='%s']", op); } return get_xpath_object(xpath, data_set->input, LOG_DEBUG); } gboolean unpack_rsc_op(resource_t *rsc, node_t *node, xmlNode *xml_op, GListPtr next, enum action_fail_response *on_fail, pe_working_set_t *data_set) { - int task_id = 0; + int task_id = 0; - const char *id = NULL; - const char *key = NULL; - const char *task = NULL; - const char *magic = NULL; - const char *actual_rc = NULL; + const char *id = NULL; + const char *key = NULL; + const char *task = NULL; + const char *magic = NULL; + const char *actual_rc = NULL; /* const char *target_rc = NULL; */ - const char *task_status = NULL; - const char *interval_s = NULL; - const char *op_version = NULL; - - int interval = 0; - int task_status_i = -2; - int actual_rc_i = 0; - int target_rc = -1; - int last_failure = 0; + const char *task_status = NULL; + const char *interval_s = NULL; + const char *op_version = NULL; + + int interval = 0; + int task_status_i = -2; + int actual_rc_i = 0; + int target_rc = -1; + int last_failure = 0; - action_t *action = NULL; - node_t *effective_node = NULL; - resource_t *failed = NULL; + action_t *action = NULL; + node_t *effective_node = NULL; + resource_t *failed = NULL; - gboolean expired = FALSE; - gboolean is_probe = FALSE; + gboolean expired = FALSE; + gboolean is_probe = FALSE; - CRM_CHECK(rsc != NULL, return FALSE); - CRM_CHECK(node != NULL, return FALSE); - CRM_CHECK(xml_op != NULL, return FALSE); - - id = ID(xml_op); - task = crm_element_value(xml_op, XML_LRM_ATTR_TASK); - task_status = crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS); - op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION); - magic = crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC); - key = crm_element_value(xml_op, XML_ATTR_TRANSITION_KEY); - - crm_element_value_int(xml_op, XML_LRM_ATTR_CALLID, &task_id); + CRM_CHECK(rsc != NULL, return FALSE); + CRM_CHECK(node != NULL, return FALSE); + CRM_CHECK(xml_op != NULL, return FALSE); + + id = ID(xml_op); + task = crm_element_value(xml_op, XML_LRM_ATTR_TASK); + task_status = crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS); + op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION); + magic = crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC); + key = crm_element_value(xml_op, XML_ATTR_TRANSITION_KEY); + + crm_element_value_int(xml_op, XML_LRM_ATTR_CALLID, &task_id); - CRM_CHECK(id != NULL, return FALSE); - CRM_CHECK(task != NULL, return FALSE); - CRM_CHECK(task_status != NULL, return FALSE); + CRM_CHECK(id != NULL, return FALSE); + CRM_CHECK(task != NULL, return FALSE); + CRM_CHECK(task_status != NULL, return FALSE); - task_status_i = crm_parse_int(task_status, NULL); + task_status_i = crm_parse_int(task_status, NULL); - CRM_CHECK(task_status_i <= LRM_OP_ERROR, return FALSE); - CRM_CHECK(task_status_i >= LRM_OP_PENDING, return FALSE); + CRM_CHECK(task_status_i <= LRM_OP_ERROR, return FALSE); + CRM_CHECK(task_status_i >= LRM_OP_PENDING, return FALSE); - if(safe_str_eq(task, CRMD_ACTION_NOTIFY)) { - /* safe to ignore these */ - return TRUE; - } + if(safe_str_eq(task, CRMD_ACTION_NOTIFY)) { + /* safe to ignore these */ + return TRUE; + } - if(rsc->failure_timeout > 0) { - int last_run = 0; + if(rsc->failure_timeout > 0) { + int last_run = 0; - if(crm_element_value_int(xml_op, "last-run", &last_run) == 0) { + if(crm_element_value_int(xml_op, "last-run", &last_run) == 0) { /* int last_change = crm_element_value_int(xml_op, "last_rc_change"); */ - time_t now = get_timet_now(data_set); - if(now > (last_run + rsc->failure_timeout)) { - expired = TRUE; - } + time_t now = get_timet_now(data_set); + if(now > (last_run + rsc->failure_timeout)) { + expired = TRUE; } } + } - crm_debug_2("Unpacking task %s/%s (call_id=%d, status=%s) on %s (role=%s)", - id, task, task_id, task_status, node->details->uname, - role2text(rsc->role)); + crm_debug_2("Unpacking task %s/%s (call_id=%d, status=%s) on %s (role=%s)", + id, task, task_id, task_status, node->details->uname, + role2text(rsc->role)); - interval_s = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL); - interval = crm_parse_int(interval_s, "0"); + interval_s = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL); + interval = crm_parse_int(interval_s, "0"); - if(interval == 0 && safe_str_eq(task, CRMD_ACTION_STATUS)) { - is_probe = TRUE; - } + if(interval == 0 && safe_str_eq(task, CRMD_ACTION_STATUS)) { + is_probe = TRUE; + } - if(node->details->unclean) { - crm_debug_2("Node %s (where %s is running) is unclean." - " Further action depends on the value of the stop's on-fail attribue", - node->details->uname, rsc->id); - } + if(node->details->unclean) { + crm_debug_2("Node %s (where %s is running) is unclean." + " Further action depends on the value of the stop's on-fail attribue", + node->details->uname, rsc->id); + } - actual_rc = crm_element_value(xml_op, XML_LRM_ATTR_RC); - CRM_CHECK(actual_rc != NULL, return FALSE); - actual_rc_i = crm_parse_int(actual_rc, NULL); + actual_rc = crm_element_value(xml_op, XML_LRM_ATTR_RC); + CRM_CHECK(actual_rc != NULL, return FALSE); + actual_rc_i = crm_parse_int(actual_rc, NULL); - if(key) { - int dummy = 0; - char *dummy_string = NULL; - decode_transition_key(key, &dummy_string, &dummy, &dummy, &target_rc); - crm_free(dummy_string); - } + if(key) { + int dummy = 0; + char *dummy_string = NULL; + decode_transition_key(key, &dummy_string, &dummy, &dummy, &target_rc); + crm_free(dummy_string); + } - if(task_status_i == LRM_OP_DONE && target_rc >= 0) { - if(target_rc == actual_rc_i) { - task_status_i = LRM_OP_DONE; + if(task_status_i == LRM_OP_DONE && target_rc >= 0) { + if(target_rc == actual_rc_i) { + task_status_i = LRM_OP_DONE; - } else { - task_status_i = LRM_OP_ERROR; - crm_debug("%s on %s returned %d (%s) instead of the expected value: %d (%s)", - id, node->details->uname, - actual_rc_i, execra_code2string(actual_rc_i), - target_rc, execra_code2string(target_rc)); - } - - } else if(task_status_i == LRM_OP_ERROR) { - /* let us decide that */ - task_status_i = LRM_OP_DONE; + } else { + task_status_i = LRM_OP_ERROR; + crm_debug("%s on %s returned %d (%s) instead of the expected value: %d (%s)", + id, node->details->uname, + actual_rc_i, execra_code2string(actual_rc_i), + target_rc, execra_code2string(target_rc)); } + + } else if(task_status_i == LRM_OP_ERROR) { + /* let us decide that */ + task_status_i = LRM_OP_DONE; + } - if(task_status_i == LRM_OP_NOTSUPPORTED) { - actual_rc_i = EXECRA_UNIMPLEMENT_FEATURE; - } + if(task_status_i == LRM_OP_NOTSUPPORTED) { + actual_rc_i = EXECRA_UNIMPLEMENT_FEATURE; + } - if(task_status_i != actual_rc_i - && rsc->failure_timeout > 0 - && get_failcount(node, rsc, &last_failure, data_set) == 0) { - if(last_failure > 0) { - action_t *clear_op = NULL; - clear_op = custom_action( - rsc, crm_concat(rsc->id, CRM_OP_CLEAR_FAILCOUNT, '_'), - CRM_OP_CLEAR_FAILCOUNT, node, FALSE, TRUE, data_set); + if(task_status_i != actual_rc_i + && rsc->failure_timeout > 0 + && get_failcount(node, rsc, &last_failure, data_set) == 0) { + if(last_failure > 0) { + action_t *clear_op = NULL; + clear_op = custom_action( + rsc, crm_concat(rsc->id, CRM_OP_CLEAR_FAILCOUNT, '_'), + CRM_OP_CLEAR_FAILCOUNT, node, FALSE, TRUE, data_set); - add_hash_param(clear_op->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE); - crm_notice("Clearing expired failcount for %s on %s", rsc->id, node->details->uname); - } + add_hash_param(clear_op->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE); + crm_notice("Clearing expired failcount for %s on %s", rsc->id, node->details->uname); } + } - if(expired - && actual_rc_i != EXECRA_NOT_RUNNING - && actual_rc_i != EXECRA_RUNNING_MASTER - && actual_rc_i != EXECRA_OK) { - crm_notice("Ignoring expired failure %s (rc=%d, magic=%s) on %s", - id, actual_rc_i, magic, node->details->uname); - goto done; - } - - /* we could clean this up significantly except for old LRMs and CRMs that - * didnt include target_rc and liked to remap status - */ - switch(actual_rc_i) { - case EXECRA_NOT_RUNNING: - if(is_probe || target_rc == actual_rc_i) { - task_status_i = LRM_OP_DONE; - rsc->role = RSC_ROLE_STOPPED; + if(expired + && actual_rc_i != EXECRA_NOT_RUNNING + && actual_rc_i != EXECRA_RUNNING_MASTER + && actual_rc_i != EXECRA_OK) { + crm_notice("Ignoring expired failure %s (rc=%d, magic=%s) on %s", + id, actual_rc_i, magic, node->details->uname); + goto done; + } + + /* we could clean this up significantly except for old LRMs and CRMs that + * didnt include target_rc and liked to remap status + */ + switch(actual_rc_i) { + case EXECRA_NOT_RUNNING: + if(is_probe || target_rc == actual_rc_i) { + task_status_i = LRM_OP_DONE; + rsc->role = RSC_ROLE_STOPPED; - /* clear any previous failure actions */ - *on_fail = action_fail_ignore; - rsc->next_role = RSC_ROLE_UNKNOWN; + /* clear any previous failure actions */ + *on_fail = action_fail_ignore; + rsc->next_role = RSC_ROLE_UNKNOWN; - } else if(safe_str_neq(task, CRMD_ACTION_STOP)) { - task_status_i = LRM_OP_ERROR; - } - break; - - case EXECRA_RUNNING_MASTER: - if(is_probe) { - task_status_i = LRM_OP_DONE; - crm_notice("Operation %s found resource %s active in master mode on %s", - id, rsc->id, node->details->uname); - - } else if(target_rc == actual_rc_i) { - /* nothing to do */ - - } else if(target_rc >= 0) { - task_status_i = LRM_OP_ERROR; - - /* legacy code for pre-0.6.5 operations */ - } else if(safe_str_neq(task, CRMD_ACTION_STATUS) - || rsc->role != RSC_ROLE_MASTER) { - task_status_i = LRM_OP_ERROR; - if(rsc->role != RSC_ROLE_MASTER) { - crm_err("%s reported %s in master mode on %s", - id, rsc->id, - node->details->uname); - } - } - rsc->role = RSC_ROLE_MASTER; - break; + } else if(safe_str_neq(task, CRMD_ACTION_STOP)) { + task_status_i = LRM_OP_ERROR; + } + break; - case EXECRA_FAILED_MASTER: - rsc->role = RSC_ROLE_MASTER; + case EXECRA_RUNNING_MASTER: + if(is_probe) { + task_status_i = LRM_OP_DONE; + crm_notice("Operation %s found resource %s active in master mode on %s", + id, rsc->id, node->details->uname); + + } else if(target_rc == actual_rc_i) { + /* nothing to do */ + + } else if(target_rc >= 0) { task_status_i = LRM_OP_ERROR; - break; - case EXECRA_UNIMPLEMENT_FEATURE: - if(interval > 0) { - task_status_i = LRM_OP_NOTSUPPORTED; - break; - } - /* else: fall through */ - case EXECRA_INSUFFICIENT_PRIV: - case EXECRA_NOT_INSTALLED: - case EXECRA_INVALID_PARAM: - effective_node = node; - /* fall through */ - case EXECRA_NOT_CONFIGURED: - failed = rsc; - if(is_not_set(rsc->flags, pe_rsc_unique)) { - failed = uber_parent(failed); + /* legacy code for pre-0.6.5 operations */ + } else if(safe_str_neq(task, CRMD_ACTION_STATUS) + || rsc->role != RSC_ROLE_MASTER) { + task_status_i = LRM_OP_ERROR; + if(rsc->role != RSC_ROLE_MASTER) { + crm_err("%s reported %s in master mode on %s", + id, rsc->id, + node->details->uname); } + } + rsc->role = RSC_ROLE_MASTER; + break; - do_crm_log(actual_rc_i==EXECRA_NOT_INSTALLED?LOG_NOTICE:LOG_ERR, - "Hard error - %s failed with rc=%d: Preventing %s from re-starting %s %s", - id, actual_rc_i, failed->id, - effective_node?"on":"anywhere", - effective_node?effective_node->details->uname:"in the cluster"); - - resource_location(failed, effective_node, -INFINITY, "hard-error", data_set); - if(is_probe) { - /* treat these like stops */ - task = CRMD_ACTION_STOP; - task_status_i = LRM_OP_DONE; - crm_xml_add(xml_op, XML_ATTR_UNAME, node->details->uname); - if(actual_rc_i != EXECRA_NOT_INSTALLED - || is_set(data_set->flags, pe_flag_symmetric_cluster)) { - if ((node->details->shutdown == FALSE) || (node->details->online == TRUE)) { - add_node_copy(data_set->failed, xml_op); - } - } - } - break; + case EXECRA_FAILED_MASTER: + rsc->role = RSC_ROLE_MASTER; + task_status_i = LRM_OP_ERROR; + break; - case EXECRA_OK: - if(is_probe && target_rc == 7) { - task_status_i = LRM_OP_DONE; - crm_notice("Operation %s found resource %s active on %s", - id, rsc->id, node->details->uname); - - /* legacy code for pre-0.6.5 operations */ - } else if(target_rc < 0 - && interval > 0 - && rsc->role == RSC_ROLE_MASTER) { - /* catch status ops that return 0 instead of 8 while they - * are supposed to be in master mode - */ - task_status_i = LRM_OP_ERROR; - } - + case EXECRA_UNIMPLEMENT_FEATURE: + if(interval > 0) { + task_status_i = LRM_OP_NOTSUPPORTED; break; + } + /* else: fall through */ + case EXECRA_INSUFFICIENT_PRIV: + case EXECRA_NOT_INSTALLED: + case EXECRA_INVALID_PARAM: + effective_node = node; + /* fall through */ + case EXECRA_NOT_CONFIGURED: + failed = rsc; + if(is_not_set(rsc->flags, pe_rsc_unique)) { + failed = uber_parent(failed); + } - default: - if(task_status_i == LRM_OP_DONE) { - crm_info("Remapping %s (rc=%d) on %s to an ERROR", - id, actual_rc_i, node->details->uname); - task_status_i = LRM_OP_ERROR; + do_crm_log(actual_rc_i==EXECRA_NOT_INSTALLED?LOG_NOTICE:LOG_ERR, + "Hard error - %s failed with rc=%d: Preventing %s from re-starting %s %s", + id, actual_rc_i, failed->id, + effective_node?"on":"anywhere", + effective_node?effective_node->details->uname:"in the cluster"); + + resource_location(failed, effective_node, -INFINITY, "hard-error", data_set); + if(is_probe) { + /* treat these like stops */ + task = CRMD_ACTION_STOP; + task_status_i = LRM_OP_DONE; + crm_xml_add(xml_op, XML_ATTR_UNAME, node->details->uname); + if(actual_rc_i != EXECRA_NOT_INSTALLED + || is_set(data_set->flags, pe_flag_symmetric_cluster)) { + if ((node->details->shutdown == FALSE) || (node->details->online == TRUE)) { + add_node_copy(data_set->failed, xml_op); + } } - } + } + break; - if(task_status_i == LRM_OP_ERROR - || task_status_i == LRM_OP_TIMEOUT - || task_status_i == LRM_OP_NOTSUPPORTED) { - action = custom_action(rsc, crm_strdup(id), task, NULL, TRUE, FALSE, data_set); - if(expired) { - crm_notice("Ignoring expired failure (calculated) %s (rc=%d, magic=%s) on %s", - id, actual_rc_i, magic, node->details->uname); - goto done; - - } else if(action->on_fail == action_fail_ignore) { - crm_warn("Remapping %s (rc=%d) on %s to DONE: ignore", - id, actual_rc_i, node->details->uname); + case EXECRA_OK: + if(is_probe && target_rc == 7) { task_status_i = LRM_OP_DONE; - } - } + crm_notice("Operation %s found resource %s active on %s", + id, rsc->id, node->details->uname); + + /* legacy code for pre-0.6.5 operations */ + } else if(target_rc < 0 + && interval > 0 + && rsc->role == RSC_ROLE_MASTER) { + /* catch status ops that return 0 instead of 8 while they + * are supposed to be in master mode + */ + task_status_i = LRM_OP_ERROR; + } + + break; + + default: + if(task_status_i == LRM_OP_DONE) { + crm_info("Remapping %s (rc=%d) on %s to an ERROR", + id, actual_rc_i, node->details->uname); + task_status_i = LRM_OP_ERROR; + } + } + + if(task_status_i == LRM_OP_ERROR + || task_status_i == LRM_OP_TIMEOUT + || task_status_i == LRM_OP_NOTSUPPORTED) { + action = custom_action(rsc, crm_strdup(id), task, NULL, TRUE, FALSE, data_set); + if(expired) { + crm_notice("Ignoring expired failure (calculated) %s (rc=%d, magic=%s) on %s", + id, actual_rc_i, magic, node->details->uname); + goto done; + + } else if(action->on_fail == action_fail_ignore) { + crm_warn("Remapping %s (rc=%d) on %s to DONE: ignore", + id, actual_rc_i, node->details->uname); + task_status_i = LRM_OP_DONE; + } + } - switch(task_status_i) { - case LRM_OP_PENDING: - if(safe_str_eq(task, CRMD_ACTION_START)) { - set_bit(rsc->flags, pe_rsc_start_pending); - set_active(rsc); + switch(task_status_i) { + case LRM_OP_PENDING: + if(safe_str_eq(task, CRMD_ACTION_START)) { + set_bit(rsc->flags, pe_rsc_start_pending); + set_active(rsc); - } else if(safe_str_eq(task, CRMD_ACTION_PROMOTE)) { - rsc->role = RSC_ROLE_MASTER; - } - /* - * Intentionally ignoring pending migrate ops here; - * haven't decided if we need to do anything special - * with them yet... - */ - break; + } else if(safe_str_eq(task, CRMD_ACTION_PROMOTE)) { + rsc->role = RSC_ROLE_MASTER; + } + /* + * Intentionally ignoring pending migrate ops here; + * haven't decided if we need to do anything special + * with them yet... + */ + break; - case LRM_OP_DONE: - crm_debug_3("%s/%s completed on %s", - rsc->id, task, node->details->uname); + case LRM_OP_DONE: + crm_debug_3("%s/%s completed on %s", + rsc->id, task, node->details->uname); - if(actual_rc_i == EXECRA_NOT_RUNNING) { - /* nothing to do */ + if(actual_rc_i == EXECRA_NOT_RUNNING) { + /* nothing to do */ - } else if(safe_str_eq(task, CRMD_ACTION_STOP)) { - rsc->role = RSC_ROLE_STOPPED; + } else if(safe_str_eq(task, CRMD_ACTION_STOP)) { + rsc->role = RSC_ROLE_STOPPED; - /* clear any previous failure actions */ - switch(*on_fail) { - case action_fail_block: - case action_fail_stop: - case action_fail_fence: - case action_fail_migrate: - case action_fail_standby: - crm_debug_2("%s.%s is not cleared by a completed stop", - rsc->id, fail2text(*on_fail)); - break; - - case action_fail_ignore: - case action_fail_recover: - *on_fail = action_fail_ignore; - rsc->next_role = RSC_ROLE_UNKNOWN; - } - - } else if(safe_str_eq(task, CRMD_ACTION_PROMOTE)) { - rsc->role = RSC_ROLE_MASTER; - - } else if(safe_str_eq(task, CRMD_ACTION_DEMOTE)) { - rsc->role = RSC_ROLE_SLAVE; - - } else if(safe_str_eq(task, CRMD_ACTION_MIGRATED)) { - rsc->role = RSC_ROLE_STARTED; + /* clear any previous failure actions */ + switch(*on_fail) { + case action_fail_block: + case action_fail_stop: + case action_fail_fence: + case action_fail_migrate: + case action_fail_standby: + crm_debug_2("%s.%s is not cleared by a completed stop", + rsc->id, fail2text(*on_fail)); + break; + + case action_fail_ignore: + case action_fail_recover: + *on_fail = action_fail_ignore; + rsc->next_role = RSC_ROLE_UNKNOWN; + } + + } else if(safe_str_eq(task, CRMD_ACTION_PROMOTE)) { + rsc->role = RSC_ROLE_MASTER; + + } else if(safe_str_eq(task, CRMD_ACTION_DEMOTE)) { + rsc->role = RSC_ROLE_SLAVE; + + } else if(safe_str_eq(task, CRMD_ACTION_MIGRATED)) { + rsc->role = RSC_ROLE_STARTED; - } else if(safe_str_eq(task, CRMD_ACTION_MIGRATE)) { - /* - * The normal sequence is (now): migrate_to(Src) -> migrate_from(Tgt) -> stop(Src) - * - * So if a migrate_to is followed by a stop, then we dont need to care what - * happended on the target node - * - * Without the stop, we need to look for a successful migrate_from. - * This would also imply we're no longer running on the source - * - * Without the stop, and without a migrate_from op we make sure the resource - * gets stopped on both source and target (assuming the target is up) - * - */ - int stop_id = 0; - xmlNode *stop_op = find_lrm_op(rsc->id, CRMD_ACTION_STOP, node->details->id, NULL, data_set); + } else if(safe_str_eq(task, CRMD_ACTION_MIGRATE)) { + /* + * The normal sequence is (now): migrate_to(Src) -> migrate_from(Tgt) -> stop(Src) + * + * So if a migrate_to is followed by a stop, then we dont need to care what + * happended on the target node + * + * Without the stop, we need to look for a successful migrate_from. + * This would also imply we're no longer running on the source + * + * Without the stop, and without a migrate_from op we make sure the resource + * gets stopped on both source and target (assuming the target is up) + * + */ + int stop_id = 0; + xmlNode *stop_op = find_lrm_op(rsc->id, CRMD_ACTION_STOP, node->details->id, NULL, data_set); - if(stop_op) { - crm_element_value_int(stop_op, XML_LRM_ATTR_CALLID, &stop_id); - } + if(stop_op) { + crm_element_value_int(stop_op, XML_LRM_ATTR_CALLID, &stop_id); + } - if(stop_op == NULL || stop_id < task_id) { - int from_rc = 0, from_status = 0; - const char *migrate_source = crm_element_value(xml_op, XML_LRM_ATTR_MIGRATE_SOURCE); - const char *migrate_target = crm_element_value(xml_op, XML_LRM_ATTR_MIGRATE_TARGET); - - node_t *target = pe_find_node_id(data_set->nodes, migrate_target); - xmlNode *migrate_from = find_lrm_op(rsc->id, CRMD_ACTION_MIGRATED, migrate_target, migrate_source, data_set); - - rsc->role = RSC_ROLE_STARTED; /* can be master? */ - if(migrate_from) { - crm_element_value_int(migrate_from, XML_LRM_ATTR_RC, &from_rc); - crm_element_value_int(migrate_from, XML_LRM_ATTR_OPSTATUS, &from_status); - crm_trace("%s op on %s exited with status=%d, rc=%d", - ID(migrate_from), migrate_target, from_status, from_rc); - } + if(stop_op == NULL || stop_id < task_id) { + int from_rc = 0, from_status = 0; + const char *migrate_source = crm_element_value(xml_op, XML_LRM_ATTR_MIGRATE_SOURCE); + const char *migrate_target = crm_element_value(xml_op, XML_LRM_ATTR_MIGRATE_TARGET); + + node_t *target = pe_find_node_id(data_set->nodes, migrate_target); + xmlNode *migrate_from = find_lrm_op(rsc->id, CRMD_ACTION_MIGRATED, migrate_target, migrate_source, data_set); + + rsc->role = RSC_ROLE_STARTED; /* can be master? */ + if(migrate_from) { + crm_element_value_int(migrate_from, XML_LRM_ATTR_RC, &from_rc); + crm_element_value_int(migrate_from, XML_LRM_ATTR_OPSTATUS, &from_status); + crm_trace("%s op on %s exited with status=%d, rc=%d", + ID(migrate_from), migrate_target, from_status, from_rc); + } - if(migrate_from && from_rc == EXECRA_OK && from_status == LRM_OP_DONE) { - crm_trace("Detected dangling migration op: %s on %s", ID(xml_op), migrate_source); - - /* all good - * just need to arrange for the stop action to get sent - * but _without_ affecting the target somehow - */ - rsc->role = RSC_ROLE_STOPPED; - rsc->dangling_migrations = g_list_prepend(rsc->dangling_migrations, node); - - } else if(migrate_from) { /* Failed */ - crm_trace("Marking active on %s %p %d", migrate_target, target, target->details->online); - if(target && target->details->online) { - native_add_running(rsc, target, data_set); - } + if(migrate_from && from_rc == EXECRA_OK && from_status == LRM_OP_DONE) { + crm_trace("Detected dangling migration op: %s on %s", ID(xml_op), migrate_source); + + /* all good + * just need to arrange for the stop action to get sent + * but _without_ affecting the target somehow + */ + rsc->role = RSC_ROLE_STOPPED; + rsc->dangling_migrations = g_list_prepend(rsc->dangling_migrations, node); - } else { /* Pending or complete but erased */ - node_t *target = pe_find_node_id(data_set->nodes, migrate_target); - - crm_trace("Marking active on %s %p %d", migrate_target, target, target->details->online); - if(target && target->details->online) { - /* TODO: One day, figure out how to complete the migration - * For now, consider it active in both locations so it gets stopped everywhere - */ - native_add_running(rsc, target, data_set); - - } else { - /* Consider it failed here - forces a restart, prevents migration */ - set_bit_inplace(rsc->flags, pe_rsc_failed); - } - } - } - - } else if(rsc->role < RSC_ROLE_STARTED) { - /* start, migrate_to and migrate_from will land here */ - crm_debug_3("%s active on %s", - rsc->id, node->details->uname); - set_active(rsc); + } else if(migrate_from) { /* Failed */ + crm_trace("Marking active on %s %p %d", migrate_target, target, target->details->online); + if(target && target->details->online) { + native_add_running(rsc, target, data_set); } - break; + + } else { /* Pending or complete but erased */ + node_t *target = pe_find_node_id(data_set->nodes, migrate_target); - case LRM_OP_ERROR: - case LRM_OP_TIMEOUT: - case LRM_OP_NOTSUPPORTED: - crm_warn("Processing failed op %s on %s: %s (%d)", - id, node->details->uname, - execra_code2string(actual_rc_i), actual_rc_i); - crm_xml_add(xml_op, XML_ATTR_UNAME, node->details->uname); - if ((node->details->shutdown == FALSE) || (node->details->online == TRUE)) { - add_node_copy(data_set->failed, xml_op); - } + crm_trace("Marking active on %s %p %d", migrate_target, target, target->details->online); + if(target && target->details->online) { + /* TODO: One day, figure out how to complete the migration + * For now, consider it active in both locations so it gets stopped everywhere + */ + native_add_running(rsc, target, data_set); - if(*on_fail < action->on_fail) { - *on_fail = action->on_fail; + } else { + /* Consider it failed here - forces a restart, prevents migration */ + set_bit_inplace(rsc->flags, pe_rsc_failed); } + } + } + + } else if(rsc->role < RSC_ROLE_STARTED) { + /* start, migrate_to and migrate_from will land here */ + crm_debug_3("%s active on %s", + rsc->id, node->details->uname); + set_active(rsc); + } + break; + + case LRM_OP_ERROR: + case LRM_OP_TIMEOUT: + case LRM_OP_NOTSUPPORTED: + crm_warn("Processing failed op %s on %s: %s (%d)", + id, node->details->uname, + execra_code2string(actual_rc_i), actual_rc_i); + crm_xml_add(xml_op, XML_ATTR_UNAME, node->details->uname); + if ((node->details->shutdown == FALSE) || (node->details->online == TRUE)) { + add_node_copy(data_set->failed, xml_op); + } - if(safe_str_eq(task, CRMD_ACTION_STOP)) { - resource_location( - rsc, node, -INFINITY, "__stop_fail__", data_set); + if(*on_fail < action->on_fail) { + *on_fail = action->on_fail; + } + + if(safe_str_eq(task, CRMD_ACTION_STOP)) { + resource_location( + rsc, node, -INFINITY, "__stop_fail__", data_set); - } else if(safe_str_eq(task, CRMD_ACTION_MIGRATED)) { - int stop_id = 0; - int migrate_id = 0; - const char *migrate_source = crm_element_value(xml_op, XML_LRM_ATTR_MIGRATE_SOURCE); - const char *migrate_target = crm_element_value(xml_op, XML_LRM_ATTR_MIGRATE_TARGET); - - xmlNode *stop_op = find_lrm_op(rsc->id, CRMD_ACTION_STOP, migrate_source, NULL, data_set); - xmlNode *migrate_op = find_lrm_op(rsc->id, CRMD_ACTION_MIGRATE, migrate_source, migrate_target, data_set); - - if(stop_op) { - crm_element_value_int(stop_op, XML_LRM_ATTR_CALLID, &stop_id); - } - if(migrate_op) { - crm_element_value_int(migrate_op, XML_LRM_ATTR_CALLID, &migrate_id); - } - - /* Get our state right */ - rsc->role = RSC_ROLE_STARTED; /* can be master? */ - - if(stop_op == NULL || stop_id < migrate_id) { - node_t *source = pe_find_node_id(data_set->nodes, migrate_source); + } else if(safe_str_eq(task, CRMD_ACTION_MIGRATED)) { + int stop_id = 0; + int migrate_id = 0; + const char *migrate_source = crm_element_value(xml_op, XML_LRM_ATTR_MIGRATE_SOURCE); + const char *migrate_target = crm_element_value(xml_op, XML_LRM_ATTR_MIGRATE_TARGET); + + xmlNode *stop_op = find_lrm_op(rsc->id, CRMD_ACTION_STOP, migrate_source, NULL, data_set); + xmlNode *migrate_op = find_lrm_op(rsc->id, CRMD_ACTION_MIGRATE, migrate_source, migrate_target, data_set); + + if(stop_op) { + crm_element_value_int(stop_op, XML_LRM_ATTR_CALLID, &stop_id); + } + if(migrate_op) { + crm_element_value_int(migrate_op, XML_LRM_ATTR_CALLID, &migrate_id); + } + + /* Get our state right */ + rsc->role = RSC_ROLE_STARTED; /* can be master? */ + + if(stop_op == NULL || stop_id < migrate_id) { + node_t *source = pe_find_node_id(data_set->nodes, migrate_source); - if(source && source->details->online) { - native_add_running(rsc, source, data_set); - } - } + if(source && source->details->online) { + native_add_running(rsc, source, data_set); + } + } - } else if(safe_str_eq(task, CRMD_ACTION_MIGRATE)) { - int stop_id = 0; - int migrate_id = 0; - const char *migrate_source = crm_element_value(xml_op, XML_LRM_ATTR_MIGRATE_SOURCE); - const char *migrate_target = crm_element_value(xml_op, XML_LRM_ATTR_MIGRATE_TARGET); - - xmlNode *stop_op = find_lrm_op(rsc->id, CRMD_ACTION_STOP, migrate_target, NULL, data_set); - xmlNode *migrate_op = find_lrm_op(rsc->id, CRMD_ACTION_MIGRATED, migrate_target, migrate_source, data_set); - - if(stop_op) { - crm_element_value_int(stop_op, XML_LRM_ATTR_CALLID, &stop_id); - } - if(migrate_op) { - crm_element_value_int(migrate_op, XML_LRM_ATTR_CALLID, &migrate_id); - } + } else if(safe_str_eq(task, CRMD_ACTION_MIGRATE)) { + int stop_id = 0; + int migrate_id = 0; + const char *migrate_source = crm_element_value(xml_op, XML_LRM_ATTR_MIGRATE_SOURCE); + const char *migrate_target = crm_element_value(xml_op, XML_LRM_ATTR_MIGRATE_TARGET); + + xmlNode *stop_op = find_lrm_op(rsc->id, CRMD_ACTION_STOP, migrate_target, NULL, data_set); + xmlNode *migrate_op = find_lrm_op(rsc->id, CRMD_ACTION_MIGRATED, migrate_target, migrate_source, data_set); + + if(stop_op) { + crm_element_value_int(stop_op, XML_LRM_ATTR_CALLID, &stop_id); + } + if(migrate_op) { + crm_element_value_int(migrate_op, XML_LRM_ATTR_CALLID, &migrate_id); + } - /* Get our state right */ - rsc->role = RSC_ROLE_STARTED; /* can be master? */ - - if(stop_op == NULL || stop_id < migrate_id) { - node_t *target = pe_find_node_id(data_set->nodes, migrate_target); - - crm_trace("Stop: %p %d, Migrated: %p %d", stop_op, stop_id, migrate_op, migrate_id); - if(target && target->details->online) { - native_add_running(rsc, target, data_set); - } - - } else if(migrate_op == NULL) { - /* Make sure it gets cleaned up, the stop may pre-date the migrate_from */ - rsc->dangling_migrations = g_list_prepend(rsc->dangling_migrations, node); - } - - } else if(safe_str_eq(task, CRMD_ACTION_PROMOTE)) { - rsc->role = RSC_ROLE_MASTER; - - } else if(safe_str_eq(task, CRMD_ACTION_DEMOTE)) { - /* - * staying in role=master ends up putting the PE/TE into a loop - * setting role=slave is not dangerous because no master will be - * promoted until the failed resource has been fully stopped - */ - crm_warn("Forcing %s to stop after a failed demote action", rsc->id); - rsc->next_role = RSC_ROLE_STOPPED; - rsc->role = RSC_ROLE_SLAVE; - - } else if(compare_version("2.0", op_version) > 0 - && safe_str_eq(task, CRMD_ACTION_START)) { - crm_warn("Compatibility handling for failed op %s on %s", - id, node->details->uname); - resource_location( - rsc, node, -INFINITY, "__legacy_start__", data_set); - } + /* Get our state right */ + rsc->role = RSC_ROLE_STARTED; /* can be master? */ - if(rsc->role < RSC_ROLE_STARTED) { - set_active(rsc); - } + if(stop_op == NULL || stop_id < migrate_id) { + node_t *target = pe_find_node_id(data_set->nodes, migrate_target); - crm_debug_2("Resource %s: role=%s, unclean=%s, on_fail=%s, fail_role=%s", - rsc->id, role2text(rsc->role), - node->details->unclean?"true":"false", - fail2text(action->on_fail), - role2text(action->fail_role)); + crm_trace("Stop: %p %d, Migrated: %p %d", stop_op, stop_id, migrate_op, migrate_id); + if(target && target->details->online) { + native_add_running(rsc, target, data_set); + } - if(action->fail_role != RSC_ROLE_STARTED - && rsc->next_role < action->fail_role) { - rsc->next_role = action->fail_role; - } + } else if(migrate_op == NULL) { + /* Make sure it gets cleaned up, the stop may pre-date the migrate_from */ + rsc->dangling_migrations = g_list_prepend(rsc->dangling_migrations, node); + } - if(action->fail_role == RSC_ROLE_STOPPED) { - int score = -INFINITY; - crm_err("Making sure %s doesn't come up again", rsc->id); - /* make sure it doesnt come up again */ - g_hash_table_destroy(rsc->allowed_nodes); - rsc->allowed_nodes = node_hash_from_list(data_set->nodes); - g_hash_table_foreach(rsc->allowed_nodes, set_node_score, &score); - } + } else if(safe_str_eq(task, CRMD_ACTION_PROMOTE)) { + rsc->role = RSC_ROLE_MASTER; + + } else if(safe_str_eq(task, CRMD_ACTION_DEMOTE)) { + /* + * staying in role=master ends up putting the PE/TE into a loop + * setting role=slave is not dangerous because no master will be + * promoted until the failed resource has been fully stopped + */ + crm_warn("Forcing %s to stop after a failed demote action", rsc->id); + rsc->next_role = RSC_ROLE_STOPPED; + rsc->role = RSC_ROLE_SLAVE; + + } else if(compare_version("2.0", op_version) > 0 + && safe_str_eq(task, CRMD_ACTION_START)) { + crm_warn("Compatibility handling for failed op %s on %s", + id, node->details->uname); + resource_location( + rsc, node, -INFINITY, "__legacy_start__", data_set); + } + + if(rsc->role < RSC_ROLE_STARTED) { + set_active(rsc); + } + + crm_debug_2("Resource %s: role=%s, unclean=%s, on_fail=%s, fail_role=%s", + rsc->id, role2text(rsc->role), + node->details->unclean?"true":"false", + fail2text(action->on_fail), + role2text(action->fail_role)); + + if(action->fail_role != RSC_ROLE_STARTED + && rsc->next_role < action->fail_role) { + rsc->next_role = action->fail_role; + } + + if(action->fail_role == RSC_ROLE_STOPPED) { + int score = -INFINITY; + crm_err("Making sure %s doesn't come up again", rsc->id); + /* make sure it doesnt come up again */ + g_hash_table_destroy(rsc->allowed_nodes); + rsc->allowed_nodes = node_hash_from_list(data_set->nodes); + g_hash_table_foreach(rsc->allowed_nodes, set_node_score, &score); + } - pe_free_action(action); - action = NULL; - break; - case LRM_OP_CANCELLED: - /* do nothing?? */ - pe_err("Dont know what to do for cancelled ops yet"); - break; - } + pe_free_action(action); + action = NULL; + break; + case LRM_OP_CANCELLED: + /* do nothing?? */ + pe_err("Dont know what to do for cancelled ops yet"); + break; + } done: - crm_debug_3("Resource %s after %s: role=%s", - rsc->id, task, role2text(rsc->role)); + crm_debug_3("Resource %s after %s: role=%s", + rsc->id, task, role2text(rsc->role)); - pe_free_action(action); + pe_free_action(action); - return TRUE; + return TRUE; } gboolean add_node_attrs(xmlNode *xml_obj, node_t *node, gboolean overwrite, pe_working_set_t *data_set) { - g_hash_table_insert(node->details->attrs, - crm_strdup("#"XML_ATTR_UNAME), - crm_strdup(node->details->uname)); - g_hash_table_insert(node->details->attrs, - crm_strdup("#"XML_ATTR_ID), - crm_strdup(node->details->id)); - if(safe_str_eq(node->details->id, data_set->dc_uuid)) { - data_set->dc_node = node; - node->details->is_dc = TRUE; - g_hash_table_insert(node->details->attrs, - crm_strdup("#"XML_ATTR_DC), - crm_strdup(XML_BOOLEAN_TRUE)); - } else { - g_hash_table_insert(node->details->attrs, - crm_strdup("#"XML_ATTR_DC), - crm_strdup(XML_BOOLEAN_FALSE)); - } + g_hash_table_insert(node->details->attrs, + crm_strdup("#"XML_ATTR_UNAME), + crm_strdup(node->details->uname)); + g_hash_table_insert(node->details->attrs, + crm_strdup("#"XML_ATTR_ID), + crm_strdup(node->details->id)); + if(safe_str_eq(node->details->id, data_set->dc_uuid)) { + data_set->dc_node = node; + node->details->is_dc = TRUE; + g_hash_table_insert(node->details->attrs, + crm_strdup("#"XML_ATTR_DC), + crm_strdup(XML_BOOLEAN_TRUE)); + } else { + g_hash_table_insert(node->details->attrs, + crm_strdup("#"XML_ATTR_DC), + crm_strdup(XML_BOOLEAN_FALSE)); + } - unpack_instance_attributes( - data_set->input, xml_obj, XML_TAG_ATTR_SETS, NULL, - node->details->attrs, NULL, overwrite, data_set->now); + unpack_instance_attributes( + data_set->input, xml_obj, XML_TAG_ATTR_SETS, NULL, + node->details->attrs, NULL, overwrite, data_set->now); - return TRUE; + return TRUE; } static GListPtr extract_operations(const char *node, const char *rsc, xmlNode *rsc_entry, gboolean active_filter) { int counter = -1; int stop_index = -1; int start_index = -1; + + xmlNode *rsc_op = NULL; GListPtr gIter = NULL; GListPtr op_list = NULL; GListPtr sorted_op_list = NULL; /* extract operations */ op_list = NULL; sorted_op_list = NULL; - xml_child_iter_filter( - rsc_entry, rsc_op, XML_LRM_TAG_RSC_OP, - crm_xml_add(rsc_op, "resource", rsc); - crm_xml_add(rsc_op, XML_ATTR_UNAME, node); - op_list = g_list_prepend(op_list, rsc_op); - ); + for(rsc_op = rsc_entry; rsc_op != NULL; rsc_op = rsc_op->next) { + if(crm_str_eq((const char *)rsc_op->name, XML_LRM_TAG_RSC_OP, TRUE)) { + crm_xml_add(rsc_op, "resource", rsc); + crm_xml_add(rsc_op, XML_ATTR_UNAME, node); + op_list = g_list_prepend(op_list, rsc_op); + } + } if(op_list == NULL) { /* if there are no operations, there is nothing to do */ return NULL; } sorted_op_list = g_list_sort(op_list, sort_op_by_callid); /* create active recurring operations as optional */ if(active_filter == FALSE) { return sorted_op_list; } op_list = NULL; calculate_active_ops(sorted_op_list, &start_index, &stop_index); gIter = sorted_op_list; for(; gIter != NULL; gIter = gIter->next) { xmlNode *rsc_op = (xmlNode*)gIter->data; counter++; if(start_index < stop_index) { crm_debug_4("Skipping %s: not active", ID(rsc_entry)); break; } else if(counter < start_index) { crm_debug_4("Skipping %s: old", ID(rsc_op)); continue; } op_list = g_list_append(op_list, rsc_op); } g_list_free(sorted_op_list); return op_list; } GListPtr find_operations( const char *rsc, const char *node, gboolean active_filter, pe_working_set_t *data_set) { GListPtr output = NULL; GListPtr intermediate = NULL; xmlNode *tmp = NULL; xmlNode *status = find_xml_node(data_set->input, XML_CIB_TAG_STATUS, TRUE); const char *uname = NULL; node_t *this_node = NULL; - xml_child_iter_filter( - status, node_state, XML_CIB_TAG_STATE, - - uname = crm_element_value(node_state, XML_ATTR_UNAME); - if(node != NULL && safe_str_neq(uname, node)) { - continue; - } + xmlNode *node_state = NULL; + for(node_state = status; node_state != NULL; node_state = node_state->next) { + if(crm_str_eq((const char *)node_state->name, XML_CIB_TAG_STATE, TRUE)) { + uname = crm_element_value(node_state, XML_ATTR_UNAME); + if(node != NULL && safe_str_neq(uname, node)) { + continue; + } - this_node = pe_find_node(data_set->nodes, uname); - CRM_CHECK(this_node != NULL, continue); + this_node = pe_find_node(data_set->nodes, uname); + CRM_CHECK(this_node != NULL, continue); - determine_online_status(node_state, this_node, data_set); + determine_online_status(node_state, this_node, data_set); - if(this_node->details->online || is_set(data_set->flags, pe_flag_stonith_enabled)) { - /* offline nodes run no resources... - * unless stonith is enabled in which case we need to - * make sure rsc start events happen after the stonith - */ - tmp = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE); - tmp = find_xml_node(tmp, XML_LRM_TAG_RESOURCES, FALSE); - - xml_child_iter_filter( - tmp, lrm_rsc, XML_LRM_TAG_RESOURCE, - const char *rsc_id = crm_element_value(lrm_rsc, XML_ATTR_ID); - if(rsc != NULL && safe_str_neq(rsc_id, rsc)) { - continue; - } + if(this_node->details->online || is_set(data_set->flags, pe_flag_stonith_enabled)) { + /* offline nodes run no resources... + * unless stonith is enabled in which case we need to + * make sure rsc start events happen after the stonith + */ + xmlNode *lrm_rsc = NULL; + tmp = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE); + tmp = find_xml_node(tmp, XML_LRM_TAG_RESOURCES, FALSE); - intermediate = extract_operations(uname, rsc_id, lrm_rsc, active_filter); - output = g_list_concat(output, intermediate); - ); + for(lrm_rsc = tmp; lrm_rsc != NULL; lrm_rsc = lrm_rsc->next) { + if(crm_str_eq((const char *)lrm_rsc->name, XML_LRM_TAG_RESOURCE, TRUE)) { + + const char *rsc_id = crm_element_value(lrm_rsc, XML_ATTR_ID); + if(rsc != NULL && safe_str_neq(rsc_id, rsc)) { + continue; + } + + intermediate = extract_operations(uname, rsc_id, lrm_rsc, active_filter); + output = g_list_concat(output, intermediate); + } + } + } } - ); + } return output; } diff --git a/lib/pengine/utils.c b/lib/pengine/utils.c index c962b1ba0e..bad93a1d45 100644 --- a/lib/pengine/utils.c +++ b/lib/pengine/utils.c @@ -1,1373 +1,1341 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include pe_working_set_t *pe_dataset = NULL; extern xmlNode *get_object_root(const char *object_type,xmlNode *the_root); void print_str_str(gpointer key, gpointer value, gpointer user_data); gboolean ghash_free_str_str(gpointer key, gpointer value, gpointer user_data); void unpack_operation( - action_t *action, xmlNode *xml_obj, pe_working_set_t* data_set); - -void -pe_free_shallow(GListPtr alist) -{ - pe_free_shallow_adv(alist, TRUE); -} - -void -pe_free_shallow_adv(GListPtr alist, gboolean with_data) -{ - GListPtr item; - GListPtr item_next = alist; - - if(with_data == FALSE && alist != NULL) { - g_list_free(alist); - return; - } - - while(item_next != NULL) { - item = item_next; - item_next = item_next->next; - - if(with_data) { -/* crm_debug_5("freeing %p", item->data); */ - crm_free(item->data); - } - - item->data = NULL; - item->next = NULL; - g_list_free_1(item); - } -} - + action_t *action, xmlNode *xml_obj, pe_working_set_t* data_set); node_t * node_copy(node_t *this_node) { - node_t *new_node = NULL; + node_t *new_node = NULL; - CRM_CHECK(this_node != NULL, return NULL); + CRM_CHECK(this_node != NULL, return NULL); - crm_malloc0(new_node, sizeof(node_t)); - CRM_ASSERT(new_node != NULL); + crm_malloc0(new_node, sizeof(node_t)); + CRM_ASSERT(new_node != NULL); - crm_debug_5("Copying %p (%s) to %p", - this_node, this_node->details->uname, new_node); + crm_debug_5("Copying %p (%s) to %p", + this_node, this_node->details->uname, new_node); - new_node->weight = this_node->weight; - new_node->fixed = this_node->fixed; - new_node->details = this_node->details; + new_node->weight = this_node->weight; + new_node->fixed = this_node->fixed; + new_node->details = this_node->details; - return new_node; + return new_node; } /* any node in list1 or list2 and not in the other gets a score of -INFINITY */ void node_list_exclude(GHashTable *hash, GListPtr list, gboolean merge_scores) { GHashTable *result = hash; node_t *other_node = NULL; GListPtr gIter = list; GHashTableIter iter; node_t *node = NULL; g_hash_table_iter_init (&iter, hash); while (g_hash_table_iter_next (&iter, NULL, (void**)&node)) { other_node = pe_find_node_id(list, node->details->id); if(other_node == NULL) { node->weight = -INFINITY; } else if(merge_scores) { node->weight = merge_weights(node->weight, other_node->weight); } } for(; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t*)gIter->data; other_node = pe_hash_table_lookup(result, node->details->id); if(other_node == NULL) { node_t *new_node = node_copy(node); new_node->weight = -INFINITY; g_hash_table_insert(result, (gpointer)new_node->details->id, new_node); } } } GHashTable * node_hash_from_list(GListPtr list) { GListPtr gIter = list; GHashTable *result = g_hash_table_new_full( g_str_hash,g_str_equal, NULL, g_hash_destroy_str); for(; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t*)gIter->data; node_t *n = node_copy(node); g_hash_table_insert(result, (gpointer)n->details->id, n); } return result; } GListPtr node_list_dup(GListPtr list1, gboolean reset, gboolean filter) { GListPtr result = NULL; GListPtr gIter = list1; for(; gIter != NULL; gIter = gIter->next) { node_t *new_node = NULL; node_t *this_node = (node_t*)gIter->data; if(filter && this_node->weight < 0) { continue; } new_node = node_copy(this_node); if(reset) { new_node->weight = 0; } if(new_node != NULL) { result = g_list_prepend(result, new_node); } } return result; } static gint sort_node_uname(gconstpointer a, gconstpointer b) { const node_t *node_a = a; const node_t *node_b = b; return strcmp(node_a->details->uname, node_b->details->uname); } void dump_node_scores_worker(int level, const char *file, const char *function, int line, resource_t *rsc, const char *comment, GHashTable *nodes) { GHashTable *hash = nodes; GHashTableIter iter; node_t *node = NULL; if(rsc) { hash = rsc->allowed_nodes; } if(rsc && is_set(rsc->flags, pe_rsc_orphan)) { /* Don't show the allocation scores for orphans */ return; } if(level == 0) { /* For now we want this in sorted order to keep the regression tests happy */ GListPtr gIter = NULL; GListPtr list = g_hash_table_get_values(hash); list = g_list_sort(list, sort_node_uname); gIter = list; for(; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t*)gIter->data; char *score = score2char(node->weight); if(rsc) { printf("%s: %s allocation score on %s: %s\n", comment, rsc->id, node->details->uname, score); } else { printf("%s: %s = %s\n", comment, node->details->uname, score); } crm_free(score); } g_list_free(list); } else { g_hash_table_iter_init (&iter, hash); while (g_hash_table_iter_next (&iter, NULL, (void**)&node)) { char *score = score2char(node->weight); if(rsc) { do_crm_log_alias(level, file, function, line, "%s: %s allocation score on %s: %s", comment, rsc->id, node->details->uname, score); } else { do_crm_log_alias(level, file, function, line, "%s: %s = %s", comment, node->details->uname, score); } crm_free(score); } } if(rsc && rsc->children) { GListPtr gIter = NULL; gIter = rsc->children; for(; gIter != NULL; gIter = gIter->next) { resource_t *child = (resource_t*)gIter->data; dump_node_scores_worker(level, file, function, line, child, comment, nodes); } } } static void append_dump_text(gpointer key, gpointer value, gpointer user_data) { char **dump_text = user_data; int len = 0; char *new_text = NULL; len = strlen(*dump_text) + strlen(" ") + strlen(key) + strlen("=") + strlen(value) + 1; crm_malloc0(new_text, len); sprintf(new_text, "%s %s=%s", *dump_text, (char *)key, (char *)value); crm_free(*dump_text); *dump_text = new_text; } void dump_node_capacity(int level, const char *comment, node_t *node) { int len = 0; char *dump_text = NULL; len = strlen(comment) + strlen(": ") + strlen(node->details->uname) + strlen(" capacity:") + 1; crm_malloc0(dump_text, len); sprintf(dump_text, "%s: %s capacity:", comment, node->details->uname); g_hash_table_foreach(node->details->utilization, append_dump_text, &dump_text); if(level == 0) { fprintf(stdout, "%s\n", dump_text); } else { do_crm_log_unlikely(level, "%s", dump_text); } crm_free(dump_text); } void dump_rsc_utilization(int level, const char *comment, resource_t *rsc, node_t *node) { int len = 0; char *dump_text = NULL; len = strlen(comment) + strlen(": ") + strlen(rsc->id) + strlen(" utilization on ") - + strlen(node->details->uname) + strlen(":") + 1; + + strlen(node->details->uname) + strlen(":") + 1; crm_malloc0(dump_text, len); sprintf(dump_text, "%s: %s utilization on %s:", comment, rsc->id, node->details->uname); g_hash_table_foreach(rsc->utilization, append_dump_text, &dump_text); if(level == 0) { fprintf(stdout, "%s\n", dump_text); } else { do_crm_log_unlikely(level, "%s", dump_text); } crm_free(dump_text); } gint sort_rsc_index(gconstpointer a, gconstpointer b) { - const resource_t *resource1 = (const resource_t*)a; - const resource_t *resource2 = (const resource_t*)b; + const resource_t *resource1 = (const resource_t*)a; + const resource_t *resource2 = (const resource_t*)b; - if(a == NULL && b == NULL) { return 0; } - if(a == NULL) { return 1; } - if(b == NULL) { return -1; } + if(a == NULL && b == NULL) { return 0; } + if(a == NULL) { return 1; } + if(b == NULL) { return -1; } - if(resource1->sort_index > resource2->sort_index) { - return -1; - } + if(resource1->sort_index > resource2->sort_index) { + return -1; + } - if(resource1->sort_index < resource2->sort_index) { - return 1; - } + if(resource1->sort_index < resource2->sort_index) { + return 1; + } - return 0; + return 0; } gint sort_rsc_priority(gconstpointer a, gconstpointer b) { - const resource_t *resource1 = (const resource_t*)a; - const resource_t *resource2 = (const resource_t*)b; + const resource_t *resource1 = (const resource_t*)a; + const resource_t *resource2 = (const resource_t*)b; - if(a == NULL && b == NULL) { return 0; } - if(a == NULL) { return 1; } - if(b == NULL) { return -1; } + if(a == NULL && b == NULL) { return 0; } + if(a == NULL) { return 1; } + if(b == NULL) { return -1; } - if(resource1->priority > resource2->priority) { - return -1; - } + if(resource1->priority > resource2->priority) { + return -1; + } - if(resource1->priority < resource2->priority) { - return 1; - } + if(resource1->priority < resource2->priority) { + return 1; + } - return 0; + return 0; } action_t * custom_action(resource_t *rsc, char *key, const char *task, node_t *on_node, gboolean optional, gboolean save_action, pe_working_set_t *data_set) { - action_t *action = NULL; - GListPtr possible_matches = NULL; - CRM_CHECK(key != NULL, return NULL); - CRM_CHECK(task != NULL, return NULL); + action_t *action = NULL; + GListPtr possible_matches = NULL; + CRM_CHECK(key != NULL, return NULL); + CRM_CHECK(task != NULL, return NULL); - if(save_action && rsc != NULL) { - possible_matches = find_actions(rsc->actions, key, on_node); - } + if(save_action && rsc != NULL) { + possible_matches = find_actions(rsc->actions, key, on_node); + } - if(possible_matches != NULL) { - crm_free(key); - - if(g_list_length(possible_matches) > 1) { - pe_warn("Action %s for %s on %s exists %d times", - task, rsc?rsc->id:"", - on_node?on_node->details->uname:"", - g_list_length(possible_matches)); - } + if(possible_matches != NULL) { + crm_free(key); - action = g_list_nth_data(possible_matches, 0); - crm_debug_4("Found existing action (%d) %s for %s on %s", - action->id, task, rsc?rsc->id:"", - on_node?on_node->details->uname:""); - g_list_free(possible_matches); + if(g_list_length(possible_matches) > 1) { + pe_warn("Action %s for %s on %s exists %d times", + task, rsc?rsc->id:"", + on_node?on_node->details->uname:"", + g_list_length(possible_matches)); } + + action = g_list_nth_data(possible_matches, 0); + crm_debug_4("Found existing action (%d) %s for %s on %s", + action->id, task, rsc?rsc->id:"", + on_node?on_node->details->uname:""); + g_list_free(possible_matches); + } - if(action == NULL) { - if(save_action) { - crm_debug_4("Creating%s action %d: %s for %s on %s", - optional?"":" manditory", data_set->action_id, key, rsc?rsc->id:"", - on_node?on_node->details->uname:""); - } + if(action == NULL) { + if(save_action) { + crm_debug_4("Creating%s action %d: %s for %s on %s", + optional?"":" manditory", data_set->action_id, key, rsc?rsc->id:"", + on_node?on_node->details->uname:""); + } - crm_malloc0(action, sizeof(action_t)); - if(save_action) { - action->id = data_set->action_id++; - } else { - action->id = 0; - } - action->rsc = rsc; - CRM_ASSERT(task != NULL); - action->task = crm_strdup(task); - if(on_node) { - action->node = node_copy(on_node); - } - action->uuid = key; + crm_malloc0(action, sizeof(action_t)); + if(save_action) { + action->id = data_set->action_id++; + } else { + action->id = 0; + } + action->rsc = rsc; + CRM_ASSERT(task != NULL); + action->task = crm_strdup(task); + if(on_node) { + action->node = node_copy(on_node); + } + action->uuid = key; - set_bit_inplace(action->flags, pe_action_failure_is_fatal); - set_bit_inplace(action->flags, pe_action_runnable); - if(optional) { - set_bit_inplace(action->flags, pe_action_optional); - } else { - clear_bit_inplace(action->flags, pe_action_optional); - } + set_bit_inplace(action->flags, pe_action_failure_is_fatal); + set_bit_inplace(action->flags, pe_action_runnable); + if(optional) { + set_bit_inplace(action->flags, pe_action_optional); + } else { + clear_bit_inplace(action->flags, pe_action_optional); + } /* Implied by crm_malloc0()... - action->actions_before = NULL; - action->actions_after = NULL; + action->actions_before = NULL; + action->actions_after = NULL; - action->pseudo = FALSE; - action->dumped = FALSE; - action->processed = FALSE; - action->seen_count = 0; + action->pseudo = FALSE; + action->dumped = FALSE; + action->processed = FALSE; + action->seen_count = 0; */ - action->extra = g_hash_table_new_full( - g_str_hash, g_str_equal, free, free); + action->extra = g_hash_table_new_full( + g_str_hash, g_str_equal, free, free); - action->meta = g_hash_table_new_full( - g_str_hash, g_str_equal, free, free); + action->meta = g_hash_table_new_full( + g_str_hash, g_str_equal, free, free); - if(save_action) { - data_set->actions = g_list_prepend( - data_set->actions, action); - } + if(save_action) { + data_set->actions = g_list_prepend( + data_set->actions, action); + } - if(rsc != NULL) { - action->op_entry = find_rsc_op_entry(rsc, key); + if(rsc != NULL) { + action->op_entry = find_rsc_op_entry(rsc, key); - unpack_operation( - action, action->op_entry, data_set); + unpack_operation( + action, action->op_entry, data_set); - if(save_action) { - rsc->actions = g_list_prepend( - rsc->actions, action); - } - } + if(save_action) { + rsc->actions = g_list_prepend( + rsc->actions, action); + } + } - if(save_action) { - crm_debug_4("Action %d created", action->id); - } + if(save_action) { + crm_debug_4("Action %d created", action->id); } + } - if(optional == FALSE && (action->flags & pe_action_optional)) { - crm_debug_2("Action %d (%s) marked manditory", - action->id, action->uuid); - clear_bit_inplace(action->flags, pe_action_optional); - } + if(optional == FALSE && (action->flags & pe_action_optional)) { + crm_debug_2("Action %d (%s) marked manditory", + action->id, action->uuid); + clear_bit_inplace(action->flags, pe_action_optional); + } - if(rsc != NULL) { - enum action_tasks a_task = text2task(action->task); - int warn_level = LOG_DEBUG_3; - if(save_action) { - warn_level = LOG_WARNING; - } - - if(is_set(action->flags, pe_action_have_node_attrs) == FALSE - && action->node != NULL - && action->op_entry != NULL) { - set_bit_inplace(action->flags, pe_action_have_node_attrs); - unpack_instance_attributes( - data_set->input, action->op_entry, XML_TAG_ATTR_SETS, - action->node->details->attrs, - action->extra, NULL, FALSE, data_set->now); - } - - if(is_set(action->flags, pe_action_pseudo)) { - /* leave untouched */ + if(rsc != NULL) { + enum action_tasks a_task = text2task(action->task); + int warn_level = LOG_DEBUG_3; + if(save_action) { + warn_level = LOG_WARNING; + } + + if(is_set(action->flags, pe_action_have_node_attrs) == FALSE + && action->node != NULL + && action->op_entry != NULL) { + set_bit_inplace(action->flags, pe_action_have_node_attrs); + unpack_instance_attributes( + data_set->input, action->op_entry, XML_TAG_ATTR_SETS, + action->node->details->attrs, + action->extra, NULL, FALSE, data_set->now); + } + + if(is_set(action->flags, pe_action_pseudo)) { + /* leave untouched */ - } else if(action->node == NULL) { - clear_bit_inplace(action->flags, pe_action_runnable); + } else if(action->node == NULL) { + clear_bit_inplace(action->flags, pe_action_runnable); - } else if(is_not_set(rsc->flags, pe_rsc_managed) - && g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL) == NULL) { - do_crm_log_unlikely(LOG_DEBUG, "Action %s (unmanaged)", - action->uuid); - set_bit_inplace(action->flags, pe_action_optional); + } else if(is_not_set(rsc->flags, pe_rsc_managed) + && g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL) == NULL) { + do_crm_log_unlikely(LOG_DEBUG, "Action %s (unmanaged)", + action->uuid); + set_bit_inplace(action->flags, pe_action_optional); /* action->runnable = FALSE; */ - } else if(action->node->details->online == FALSE) { - clear_bit_inplace(action->flags, pe_action_runnable); - do_crm_log(warn_level, "Action %s on %s is unrunnable (offline)", - action->uuid, action->node->details->uname); - if(is_set(action->rsc->flags, pe_rsc_managed) - && save_action - && a_task == stop_rsc) { - do_crm_log(warn_level, "Marking node %s unclean", - action->node->details->uname); - action->node->details->unclean = TRUE; - } + } else if(action->node->details->online == FALSE) { + clear_bit_inplace(action->flags, pe_action_runnable); + do_crm_log(warn_level, "Action %s on %s is unrunnable (offline)", + action->uuid, action->node->details->uname); + if(is_set(action->rsc->flags, pe_rsc_managed) + && save_action + && a_task == stop_rsc) { + do_crm_log(warn_level, "Marking node %s unclean", + action->node->details->uname); + action->node->details->unclean = TRUE; + } - } else if(action->node->details->pending) { - clear_bit_inplace(action->flags, pe_action_runnable); - do_crm_log(warn_level, "Action %s on %s is unrunnable (pending)", - action->uuid, action->node->details->uname); - - } else if(action->needs == rsc_req_nothing) { - crm_debug_3("Action %s doesnt require anything", - action->uuid); - set_bit_inplace(action->flags, pe_action_runnable); + } else if(action->node->details->pending) { + clear_bit_inplace(action->flags, pe_action_runnable); + do_crm_log(warn_level, "Action %s on %s is unrunnable (pending)", + action->uuid, action->node->details->uname); + + } else if(action->needs == rsc_req_nothing) { + crm_debug_3("Action %s doesnt require anything", + action->uuid); + set_bit_inplace(action->flags, pe_action_runnable); #if 0 - /* - * No point checking this - * - if we dont have quorum we cant stonith anyway - */ - } else if(action->needs == rsc_req_stonith) { - crm_debug_3("Action %s requires only stonith", action->uuid); - action->runnable = TRUE; + /* + * No point checking this + * - if we dont have quorum we cant stonith anyway + */ + } else if(action->needs == rsc_req_stonith) { + crm_debug_3("Action %s requires only stonith", action->uuid); + action->runnable = TRUE; #endif - } else if(is_set(data_set->flags, pe_flag_have_quorum) == FALSE - && data_set->no_quorum_policy == no_quorum_stop) { - clear_bit_inplace(action->flags, pe_action_runnable); - crm_debug("%s\t%s (cancelled : quorum)", - action->node->details->uname, - action->uuid); + } else if(is_set(data_set->flags, pe_flag_have_quorum) == FALSE + && data_set->no_quorum_policy == no_quorum_stop) { + clear_bit_inplace(action->flags, pe_action_runnable); + crm_debug("%s\t%s (cancelled : quorum)", + action->node->details->uname, + action->uuid); - } else if(is_set(data_set->flags, pe_flag_have_quorum) == FALSE - && data_set->no_quorum_policy == no_quorum_freeze) { - crm_debug_3("Check resource is already active"); - if(rsc->fns->active(rsc, TRUE) == FALSE) { - clear_bit_inplace(action->flags, pe_action_runnable); - crm_debug("%s\t%s (cancelled : quorum freeze)", - action->node->details->uname, - action->uuid); - } - - } else { - crm_debug_3("Action %s is runnable", action->uuid); - set_bit_inplace(action->flags, pe_action_runnable); - } - - if(save_action) { - switch(a_task) { - case stop_rsc: - set_bit(rsc->flags, pe_rsc_stopping); - break; - case start_rsc: - clear_bit(rsc->flags, pe_rsc_starting); - if(is_set(action->flags, pe_action_runnable)) { - set_bit(rsc->flags, pe_rsc_starting); - } - break; - default: - break; - } - } + } else if(is_set(data_set->flags, pe_flag_have_quorum) == FALSE + && data_set->no_quorum_policy == no_quorum_freeze) { + crm_debug_3("Check resource is already active"); + if(rsc->fns->active(rsc, TRUE) == FALSE) { + clear_bit_inplace(action->flags, pe_action_runnable); + crm_debug("%s\t%s (cancelled : quorum freeze)", + action->node->details->uname, + action->uuid); + } + + } else { + crm_debug_3("Action %s is runnable", action->uuid); + set_bit_inplace(action->flags, pe_action_runnable); + } + + if(save_action) { + switch(a_task) { + case stop_rsc: + set_bit(rsc->flags, pe_rsc_stopping); + break; + case start_rsc: + clear_bit(rsc->flags, pe_rsc_starting); + if(is_set(action->flags, pe_action_runnable)) { + set_bit(rsc->flags, pe_rsc_starting); + } + break; + default: + break; + } } - return action; + } + return action; } void unpack_operation( - action_t *action, xmlNode *xml_obj, pe_working_set_t* data_set) + action_t *action, xmlNode *xml_obj, pe_working_set_t* data_set) { - int value_i = 0; - unsigned long long interval = 0; - unsigned long long start_delay = 0; - char *value_ms = NULL; - const char *class = NULL; - const char *value = NULL; - const char *field = NULL; + int value_i = 0; + unsigned long long interval = 0; + unsigned long long start_delay = 0; + char *value_ms = NULL; + const char *class = NULL; + const char *value = NULL; + const char *field = NULL; - CRM_CHECK(action->rsc != NULL, return); + CRM_CHECK(action->rsc != NULL, return); - unpack_instance_attributes(data_set->input, data_set->op_defaults, XML_TAG_META_SETS, NULL, - action->meta, NULL, FALSE, data_set->now); + unpack_instance_attributes(data_set->input, data_set->op_defaults, XML_TAG_META_SETS, NULL, + action->meta, NULL, FALSE, data_set->now); - xml_prop_iter(xml_obj, name, value, - if (value != NULL) { - g_hash_table_replace(action->meta, crm_strdup(name), crm_strdup(value)); - } - ); + xml_prop_iter(xml_obj, name, value, + if (value != NULL) { + g_hash_table_replace(action->meta, crm_strdup(name), crm_strdup(value)); + } + ); - unpack_instance_attributes(data_set->input, xml_obj, XML_TAG_META_SETS, - NULL, action->meta, NULL, FALSE, data_set->now); + unpack_instance_attributes(data_set->input, xml_obj, XML_TAG_META_SETS, + NULL, action->meta, NULL, FALSE, data_set->now); - unpack_instance_attributes(data_set->input, xml_obj, XML_TAG_ATTR_SETS, - NULL, action->meta, NULL, FALSE, data_set->now); + unpack_instance_attributes(data_set->input, xml_obj, XML_TAG_ATTR_SETS, + NULL, action->meta, NULL, FALSE, data_set->now); - g_hash_table_remove(action->meta, "id"); + g_hash_table_remove(action->meta, "id"); - class = g_hash_table_lookup(action->rsc->meta, "class"); + class = g_hash_table_lookup(action->rsc->meta, "class"); - value = g_hash_table_lookup(action->meta, "requires"); - if(safe_str_eq(class, "stonith")) { - action->needs = rsc_req_nothing; - value = "nothing (fencing op)"; + value = g_hash_table_lookup(action->meta, "requires"); + if(safe_str_eq(class, "stonith")) { + action->needs = rsc_req_nothing; + value = "nothing (fencing op)"; - } else if(value == NULL && safe_str_neq(action->task, CRMD_ACTION_START)) { - action->needs = rsc_req_nothing; - value = "nothing (default)"; + } else if(value == NULL && safe_str_neq(action->task, CRMD_ACTION_START)) { + action->needs = rsc_req_nothing; + value = "nothing (default)"; - } else if(safe_str_eq(value, "nothing")) { - action->needs = rsc_req_nothing; + } else if(safe_str_eq(value, "nothing")) { + action->needs = rsc_req_nothing; - } else if(safe_str_eq(value, "quorum")) { - action->needs = rsc_req_quorum; + } else if(safe_str_eq(value, "quorum")) { + action->needs = rsc_req_quorum; - } else if(safe_str_eq(value, "fencing")) { - action->needs = rsc_req_stonith; + } else if(safe_str_eq(value, "fencing")) { + action->needs = rsc_req_stonith; - } else if(data_set->no_quorum_policy == no_quorum_ignore) { - action->needs = rsc_req_nothing; - value = "nothing (default)"; + } else if(data_set->no_quorum_policy == no_quorum_ignore) { + action->needs = rsc_req_nothing; + value = "nothing (default)"; - } else if(data_set->no_quorum_policy == no_quorum_freeze - && is_set(data_set->flags, pe_flag_stonith_enabled)) { - action->needs = rsc_req_stonith; - value = "fencing (default)"; + } else if(data_set->no_quorum_policy == no_quorum_freeze + && is_set(data_set->flags, pe_flag_stonith_enabled)) { + action->needs = rsc_req_stonith; + value = "fencing (default)"; - } else { - action->needs = rsc_req_quorum; - value = "quorum (default)"; - } + } else { + action->needs = rsc_req_quorum; + value = "quorum (default)"; + } - crm_debug_3("\tAction %s requires: %s", action->task, value); + crm_debug_3("\tAction %s requires: %s", action->task, value); - value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ON_FAIL); - if(safe_str_eq(action->task, CRMD_ACTION_STOP) - && safe_str_eq(value, "standby")) { - crm_config_err("on-fail=standby is not allowed for stop actions: %s", action->rsc->id); - value = NULL; - } + value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ON_FAIL); + if(safe_str_eq(action->task, CRMD_ACTION_STOP) + && safe_str_eq(value, "standby")) { + crm_config_err("on-fail=standby is not allowed for stop actions: %s", action->rsc->id); + value = NULL; + } - if(value == NULL) { + if(value == NULL) { - } else if(safe_str_eq(value, "block")) { - action->on_fail = action_fail_block; + } else if(safe_str_eq(value, "block")) { + action->on_fail = action_fail_block; - } else if(safe_str_eq(value, "fence")) { - action->on_fail = action_fail_fence; - value = "node fencing"; + } else if(safe_str_eq(value, "fence")) { + action->on_fail = action_fail_fence; + value = "node fencing"; - if(is_set(data_set->flags, pe_flag_stonith_enabled) == FALSE) { - crm_config_err("Specifying on_fail=fence and" - " stonith-enabled=false makes no sense"); - action->on_fail = action_fail_stop; - action->fail_role = RSC_ROLE_STOPPED; - value = "stop resource"; - } + if(is_set(data_set->flags, pe_flag_stonith_enabled) == FALSE) { + crm_config_err("Specifying on_fail=fence and" + " stonith-enabled=false makes no sense"); + action->on_fail = action_fail_stop; + action->fail_role = RSC_ROLE_STOPPED; + value = "stop resource"; + } - } else if(safe_str_eq(value, "standby")) { - action->on_fail = action_fail_standby; - value = "node standby"; - - } else if(safe_str_eq(value, "ignore") - || safe_str_eq(value, "nothing")) { - action->on_fail = action_fail_ignore; - value = "ignore"; - - } else if(safe_str_eq(value, "migrate")) { - action->on_fail = action_fail_migrate; - value = "force migration"; + } else if(safe_str_eq(value, "standby")) { + action->on_fail = action_fail_standby; + value = "node standby"; + + } else if(safe_str_eq(value, "ignore") + || safe_str_eq(value, "nothing")) { + action->on_fail = action_fail_ignore; + value = "ignore"; + + } else if(safe_str_eq(value, "migrate")) { + action->on_fail = action_fail_migrate; + value = "force migration"; - } else if(safe_str_eq(value, "stop")) { - action->on_fail = action_fail_stop; - action->fail_role = RSC_ROLE_STOPPED; - value = "stop resource"; + } else if(safe_str_eq(value, "stop")) { + action->on_fail = action_fail_stop; + action->fail_role = RSC_ROLE_STOPPED; + value = "stop resource"; - } else if(safe_str_eq(value, "restart")) { - action->on_fail = action_fail_recover; - value = "restart (and possibly migrate)"; + } else if(safe_str_eq(value, "restart")) { + action->on_fail = action_fail_recover; + value = "restart (and possibly migrate)"; - } else { - pe_err("Resource %s: Unknown failure type (%s)", - action->rsc->id, value); - value = NULL; - } + } else { + pe_err("Resource %s: Unknown failure type (%s)", + action->rsc->id, value); + value = NULL; + } - /* defaults */ - if(value == NULL && safe_str_eq(action->task, CRMD_ACTION_STOP)) { - if(is_set(data_set->flags, pe_flag_stonith_enabled)) { - action->on_fail = action_fail_fence; - value = "resource fence (default)"; + /* defaults */ + if(value == NULL && safe_str_eq(action->task, CRMD_ACTION_STOP)) { + if(is_set(data_set->flags, pe_flag_stonith_enabled)) { + action->on_fail = action_fail_fence; + value = "resource fence (default)"; - } else { - action->on_fail = action_fail_block; - value = "resource block (default)"; - } - - } else if(value == NULL) { - action->on_fail = action_fail_recover; - value = "restart (and possibly migrate) (default)"; + } else { + action->on_fail = action_fail_block; + value = "resource block (default)"; } + + } else if(value == NULL) { + action->on_fail = action_fail_recover; + value = "restart (and possibly migrate) (default)"; + } - crm_debug_3("\t%s failure handling: %s", action->task, value); + crm_debug_3("\t%s failure handling: %s", action->task, value); - value = NULL; - if(xml_obj != NULL) { - value = g_hash_table_lookup(action->meta, "role_after_failure"); - } - if(value != NULL && action->fail_role == RSC_ROLE_UNKNOWN) { - action->fail_role = text2role(value); - } - /* defaults */ - if(action->fail_role == RSC_ROLE_UNKNOWN) { - if(safe_str_eq(action->task, CRMD_ACTION_PROMOTE)) { - action->fail_role = RSC_ROLE_SLAVE; - } else { - action->fail_role = RSC_ROLE_STARTED; - } - } - crm_debug_3("\t%s failure results in: %s", - action->task, role2text(action->fail_role)); - - field = XML_LRM_ATTR_INTERVAL; - value = g_hash_table_lookup(action->meta, field); - if(value != NULL) { - interval = crm_get_interval(value); - if(interval > 0) { - value_ms = crm_itoa(interval); - g_hash_table_replace(action->meta, crm_strdup(field), value_ms); - - } else { - g_hash_table_remove(action->meta, field); - } + value = NULL; + if(xml_obj != NULL) { + value = g_hash_table_lookup(action->meta, "role_after_failure"); + } + if(value != NULL && action->fail_role == RSC_ROLE_UNKNOWN) { + action->fail_role = text2role(value); + } + /* defaults */ + if(action->fail_role == RSC_ROLE_UNKNOWN) { + if(safe_str_eq(action->task, CRMD_ACTION_PROMOTE)) { + action->fail_role = RSC_ROLE_SLAVE; + } else { + action->fail_role = RSC_ROLE_STARTED; } + } + crm_debug_3("\t%s failure results in: %s", + action->task, role2text(action->fail_role)); - field = XML_OP_ATTR_START_DELAY; - value = g_hash_table_lookup(action->meta, field); - if(value != NULL) { - value_i = crm_get_msec(value); - if(value_i < 0) { - value_i = 0; - } - start_delay = value_i; - value_ms = crm_itoa(value_i); - g_hash_table_replace(action->meta, crm_strdup(field), value_ms); - - } else if(interval > 0 && g_hash_table_lookup(action->meta, XML_OP_ATTR_ORIGIN)) { - char *date_str = NULL; - char *date_str_mutable = NULL; - ha_time_t *origin = NULL; - value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ORIGIN); - date_str = crm_strdup(value); - date_str_mutable = date_str; - origin = parse_date(&date_str_mutable); - crm_free(date_str); - - if(origin == NULL) { - crm_config_err("Operation %s contained an invalid "XML_OP_ATTR_ORIGIN": %s", - ID(xml_obj), value); + field = XML_LRM_ATTR_INTERVAL; + value = g_hash_table_lookup(action->meta, field); + if(value != NULL) { + interval = crm_get_interval(value); + if(interval > 0) { + value_ms = crm_itoa(interval); + g_hash_table_replace(action->meta, crm_strdup(field), value_ms); - } else { - ha_time_t *delay = NULL; - int rc = compare_date(origin, data_set->now); - unsigned long long delay_s = 0; - - while(rc < 0) { - add_seconds(origin, interval/1000); - rc = compare_date(origin, data_set->now); - } - - delay = subtract_time(origin, data_set->now); - delay_s = date_in_seconds(delay); - /* log_date(LOG_DEBUG_5, "delay", delay, ha_log_date|ha_log_time|ha_log_local); */ - - crm_info("Calculated a start delay of %llus for %s", delay_s, ID(xml_obj)); - g_hash_table_replace(action->meta, crm_strdup(XML_OP_ATTR_START_DELAY), crm_itoa(delay_s * 1000)); - start_delay = delay_s * 1000; - free_ha_date(origin); - free_ha_date(delay); - } + } else { + g_hash_table_remove(action->meta, field); } - + } - field = XML_ATTR_TIMEOUT; - value = g_hash_table_lookup(action->meta, field); - if(value == NULL) { - value = pe_pref( - data_set->config_hash, "default-action-timeout"); - } + field = XML_OP_ATTR_START_DELAY; + value = g_hash_table_lookup(action->meta, field); + if(value != NULL) { value_i = crm_get_msec(value); if(value_i < 0) { - value_i = 0; + value_i = 0; } - value_i += start_delay; + start_delay = value_i; value_ms = crm_itoa(value_i); g_hash_table_replace(action->meta, crm_strdup(field), value_ms); + + } else if(interval > 0 && g_hash_table_lookup(action->meta, XML_OP_ATTR_ORIGIN)) { + char *date_str = NULL; + char *date_str_mutable = NULL; + ha_time_t *origin = NULL; + value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ORIGIN); + date_str = crm_strdup(value); + date_str_mutable = date_str; + origin = parse_date(&date_str_mutable); + crm_free(date_str); + + if(origin == NULL) { + crm_config_err("Operation %s contained an invalid "XML_OP_ATTR_ORIGIN": %s", + ID(xml_obj), value); + + } else { + ha_time_t *delay = NULL; + int rc = compare_date(origin, data_set->now); + unsigned long long delay_s = 0; + + while(rc < 0) { + add_seconds(origin, interval/1000); + rc = compare_date(origin, data_set->now); + } + + delay = subtract_time(origin, data_set->now); + delay_s = date_in_seconds(delay); + /* log_date(LOG_DEBUG_5, "delay", delay, ha_log_date|ha_log_time|ha_log_local); */ + + crm_info("Calculated a start delay of %llus for %s", delay_s, ID(xml_obj)); + g_hash_table_replace(action->meta, crm_strdup(XML_OP_ATTR_START_DELAY), crm_itoa(delay_s * 1000)); + start_delay = delay_s * 1000; + free_ha_date(origin); + free_ha_date(delay); + } + } + + + field = XML_ATTR_TIMEOUT; + value = g_hash_table_lookup(action->meta, field); + if(value == NULL) { + value = pe_pref( + data_set->config_hash, "default-action-timeout"); + } + value_i = crm_get_msec(value); + if(value_i < 0) { + value_i = 0; + } + value_i += start_delay; + value_ms = crm_itoa(value_i); + g_hash_table_replace(action->meta, crm_strdup(field), value_ms); } xmlNode * find_rsc_op_entry(resource_t *rsc, const char *key) { - int number = 0; - gboolean do_retry = TRUE; - char *local_key = NULL; - const char *name = NULL; - const char *value = NULL; - const char *interval = NULL; - char *match_key = NULL; - xmlNode *op = NULL; + int number = 0; + gboolean do_retry = TRUE; + char *local_key = NULL; + const char *name = NULL; + const char *value = NULL; + const char *interval = NULL; + char *match_key = NULL; + xmlNode *op = NULL; + xmlNode *operation = NULL; retry: - xml_child_iter_filter( - rsc->ops_xml, operation, "op", - - name = crm_element_value(operation, "name"); - interval = crm_element_value(operation, XML_LRM_ATTR_INTERVAL); - value = crm_element_value(operation, "enabled"); - if(value && crm_is_true(value) == FALSE) { - continue; - } - - number = crm_get_interval(interval); - if(number < 0) { - continue; - } + for(operation = rsc->ops_xml; operation != NULL; operation = operation->next) { + if(crm_str_eq((const char *)operation->name, "op", TRUE)) { + name = crm_element_value(operation, "name"); + interval = crm_element_value(operation, XML_LRM_ATTR_INTERVAL); + value = crm_element_value(operation, "enabled"); + if(value && crm_is_true(value) == FALSE) { + continue; + } + + number = crm_get_interval(interval); + if(number < 0) { + continue; + } - match_key = generate_op_key(rsc->id, name, number); - - if(safe_str_eq(key, match_key)) { - op = operation; - } - crm_free(match_key); - - if(op != NULL) { - crm_free(local_key); - return op; - } - ); - - crm_free(local_key); - if(do_retry == FALSE) { - return NULL; + match_key = generate_op_key(rsc->id, name, number); + + if(safe_str_eq(key, match_key)) { + op = operation; + } + crm_free(match_key); + + if(op != NULL) { + crm_free(local_key); + return op; + } } + } + + crm_free(local_key); + if(do_retry == FALSE) { + return NULL; + } - do_retry = FALSE; - if(strstr(key, CRMD_ACTION_MIGRATE) || strstr(key, CRMD_ACTION_MIGRATED)) { - local_key = generate_op_key(rsc->id, "migrate", 0); - key = local_key; - goto retry; + do_retry = FALSE; + if(strstr(key, CRMD_ACTION_MIGRATE) || strstr(key, CRMD_ACTION_MIGRATED)) { + local_key = generate_op_key(rsc->id, "migrate", 0); + key = local_key; + goto retry; - } else if(strstr(key, "_notify_")) { - local_key = generate_op_key(rsc->id, "notify", 0); - key = local_key; - goto retry; - } + } else if(strstr(key, "_notify_")) { + local_key = generate_op_key(rsc->id, "notify", 0); + key = local_key; + goto retry; + } - return NULL; + return NULL; } void print_node(const char *pre_text, node_t *node, gboolean details) { - if(node == NULL) { - crm_debug_4("%s%s: ", - pre_text==NULL?"":pre_text, - pre_text==NULL?"":": "); - return; - } + if(node == NULL) { + crm_debug_4("%s%s: ", + pre_text==NULL?"":pre_text, + pre_text==NULL?"":": "); + return; + } + + crm_debug_4("%s%s%sNode %s: (weight=%d, fixed=%s)", + pre_text==NULL?"":pre_text, + pre_text==NULL?"":": ", + node->details==NULL?"error ":node->details->online?"":"Unavailable/Unclean ", + node->details->uname, + node->weight, + node->fixed?"True":"False"); + + if(details && node != NULL && node->details != NULL) { + char *pe_mutable = crm_strdup("\t\t"); + GListPtr gIter = node->details->running_rsc; + crm_debug_4("\t\t===Node Attributes"); + g_hash_table_foreach(node->details->attrs, + print_str_str, pe_mutable); + crm_free(pe_mutable); + + crm_debug_4("\t\t=== Resources"); - crm_debug_4("%s%s%sNode %s: (weight=%d, fixed=%s)", - pre_text==NULL?"":pre_text, - pre_text==NULL?"":": ", - node->details==NULL?"error ":node->details->online?"":"Unavailable/Unclean ", - node->details->uname, - node->weight, - node->fixed?"True":"False"); - - if(details && node != NULL && node->details != NULL) { - char *pe_mutable = crm_strdup("\t\t"); - GListPtr gIter = node->details->running_rsc; - crm_debug_4("\t\t===Node Attributes"); - g_hash_table_foreach(node->details->attrs, - print_str_str, pe_mutable); - crm_free(pe_mutable); - - crm_debug_4("\t\t=== Resources"); - - for(; gIter != NULL; gIter = gIter->next) { - resource_t *rsc = (resource_t*)gIter->data; - print_resource(LOG_DEBUG_4, "\t\t", rsc, FALSE); - } + for(; gIter != NULL; gIter = gIter->next) { + resource_t *rsc = (resource_t*)gIter->data; + print_resource(LOG_DEBUG_4, "\t\t", rsc, FALSE); } + } } /* * Used by the HashTable for-loop */ void print_str_str(gpointer key, gpointer value, gpointer user_data) { - crm_debug_4("%s%s %s ==> %s", - user_data==NULL?"":(char*)user_data, - user_data==NULL?"":": ", - (char*)key, - (char*)value); + crm_debug_4("%s%s %s ==> %s", + user_data==NULL?"":(char*)user_data, + user_data==NULL?"":": ", + (char*)key, + (char*)value); } void print_resource( - int log_level, const char *pre_text, resource_t *rsc, gboolean details) + int log_level, const char *pre_text, resource_t *rsc, gboolean details) { - long options = pe_print_log; + long options = pe_print_log; - if(rsc == NULL) { - do_crm_log(log_level-1, "%s%s: ", - pre_text==NULL?"":pre_text, - pre_text==NULL?"":": "); - return; - } - if(details) { - options |= pe_print_details; - } - rsc->fns->print(rsc, pre_text, options, &log_level); + if(rsc == NULL) { + do_crm_log(log_level-1, "%s%s: ", + pre_text==NULL?"":pre_text, + pre_text==NULL?"":": "); + return; + } + if(details) { + options |= pe_print_details; + } + rsc->fns->print(rsc, pre_text, options, &log_level); } void pe_free_action(action_t *action) { - if(action == NULL) { - return; - } - pe_free_shallow(action->actions_before);/* action_warpper_t* */ - pe_free_shallow(action->actions_after); /* action_warpper_t* */ - if(action->extra) { - g_hash_table_destroy(action->extra); - } - if(action->meta) { - g_hash_table_destroy(action->meta); - } - crm_free(action->task); - crm_free(action->uuid); - crm_free(action->node); - crm_free(action); + if(action == NULL) { + return; + } + slist_basic_destroy(action->actions_before);/* action_warpper_t* */ + slist_basic_destroy(action->actions_after); /* action_warpper_t* */ + if(action->extra) { + g_hash_table_destroy(action->extra); + } + if(action->meta) { + g_hash_table_destroy(action->meta); + } + crm_free(action->task); + crm_free(action->uuid); + crm_free(action->node); + crm_free(action); } GListPtr find_recurring_actions(GListPtr input, node_t *not_on_node) { const char *value = NULL; GListPtr result = NULL; GListPtr gIter = input; CRM_CHECK(input != NULL, return NULL); for(; gIter != NULL; gIter = gIter->next) { action_t *action = (action_t*)gIter->data; value = g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL); if(value == NULL) { /* skip */ } else if(safe_str_eq(value, "0")) { /* skip */ } else if(safe_str_eq(CRMD_ACTION_CANCEL, action->task)) { /* skip */ } else if(not_on_node == NULL) { crm_debug_5("(null) Found: %s", action->uuid); result = g_list_prepend(result, action); } else if(action->node == NULL) { /* skip */ } else if(action->node->details != not_on_node->details) { crm_debug_5("Found: %s", action->uuid); result = g_list_prepend(result, action); } } return result; } action_t * find_first_action(GListPtr input, const char *uuid, const char *task, node_t *on_node) { GListPtr gIter = input; CRM_CHECK(uuid || task, return NULL); for(; gIter != NULL; gIter = gIter->next) { action_t *action = (action_t*)gIter->data; if(uuid != NULL && safe_str_neq(uuid, action->uuid)) { continue; } else if(task != NULL && safe_str_neq(task, action->task)) { continue; } else if(on_node == NULL) { return action; } else if(action->node == NULL) { continue; } else if(on_node->details == action->node->details) { return action; } } return NULL; } GListPtr find_actions(GListPtr input, const char *key, node_t *on_node) { GListPtr gIter = input; GListPtr result = NULL; CRM_CHECK(key != NULL, return NULL); for(; gIter != NULL; gIter = gIter->next) { action_t *action = (action_t*)gIter->data; crm_debug_5("Matching %s against %s", key, action->uuid); if(safe_str_neq(key, action->uuid)) { continue; } else if(on_node == NULL) { result = g_list_prepend(result, action); } else if(action->node == NULL) { /* skip */ crm_debug_2("While looking for %s action on %s, " "found an unallocated one. Assigning" " it to the requested node...", key, on_node->details->uname); action->node = node_copy(on_node); result = g_list_prepend(result, action); } else if(on_node->details == action->node->details) { result = g_list_prepend(result, action); } } return result; } GListPtr find_actions_exact(GListPtr input, const char *key, node_t *on_node) { GListPtr gIter = input; GListPtr result = NULL; CRM_CHECK(key != NULL, return NULL); for(; gIter != NULL; gIter = gIter->next) { action_t *action = (action_t*)gIter->data; crm_debug_5("Matching %s against %s", key, action->uuid); if(safe_str_neq(key, action->uuid)) { crm_debug_3("Key mismatch: %s vs. %s", key, action->uuid); continue; } else if(on_node == NULL || action->node == NULL) { crm_debug_3("on_node=%p, action->node=%p", on_node, action->node); continue; } else if(safe_str_eq(on_node->details->id, action->node->details->id)) { result = g_list_prepend(result, action); } crm_debug_2("Node mismatch: %s vs. %s", on_node->details->id, action->node->details->id); } return result; } static void resource_node_score(resource_t *rsc, node_t *node, int score, const char *tag) { - node_t *match = NULL; + node_t *match = NULL; - if(rsc->children) { - GListPtr gIter = rsc->children; - for(; gIter != NULL; gIter = gIter->next) { - resource_t *child_rsc = (resource_t*)gIter->data; + if(rsc->children) { + GListPtr gIter = rsc->children; + for(; gIter != NULL; gIter = gIter->next) { + resource_t *child_rsc = (resource_t*)gIter->data; - resource_node_score(child_rsc, node, score, tag); - } + resource_node_score(child_rsc, node, score, tag); } + } - crm_debug_2("Setting %s for %s on %s: %d", - tag, rsc->id, node->details->uname, score); - match = pe_hash_table_lookup(rsc->allowed_nodes, node->details->id); - if(match == NULL) { - match = node_copy(node); - match->weight = merge_weights(score, node->weight); - g_hash_table_insert(rsc->allowed_nodes, (gpointer)match->details->id, match); - } - match->weight = merge_weights(match->weight, score); + crm_debug_2("Setting %s for %s on %s: %d", + tag, rsc->id, node->details->uname, score); + match = pe_hash_table_lookup(rsc->allowed_nodes, node->details->id); + if(match == NULL) { + match = node_copy(node); + match->weight = merge_weights(score, node->weight); + g_hash_table_insert(rsc->allowed_nodes, (gpointer)match->details->id, match); + } + match->weight = merge_weights(match->weight, score); } void resource_location(resource_t *rsc, node_t *node, int score, const char *tag, pe_working_set_t *data_set) { - if(node != NULL) { - resource_node_score(rsc, node, score, tag); - - } else if(data_set != NULL) { - GListPtr gIter = data_set->nodes; - for(; gIter != NULL; gIter = gIter->next) { - node_t *node = (node_t*)gIter->data; - resource_node_score(rsc, node, score, tag); - } + if(node != NULL) { + resource_node_score(rsc, node, score, tag); + + } else if(data_set != NULL) { + GListPtr gIter = data_set->nodes; + for(; gIter != NULL; gIter = gIter->next) { + node_t *node = (node_t*)gIter->data; + resource_node_score(rsc, node, score, tag); + } - } else { - GHashTableIter iter; - node_t *node = NULL; - g_hash_table_iter_init (&iter, rsc->allowed_nodes); - while (g_hash_table_iter_next (&iter, NULL, (void**)&node)) { - resource_node_score(rsc, node, score, tag); - } + } else { + GHashTableIter iter; + node_t *node = NULL; + g_hash_table_iter_init (&iter, rsc->allowed_nodes); + while (g_hash_table_iter_next (&iter, NULL, (void**)&node)) { + resource_node_score(rsc, node, score, tag); } + } - if(node == NULL && score == -INFINITY) { - if(rsc->allocated_to) { - crm_info("Deallocating %s from %s", rsc->id, rsc->allocated_to->details->uname); - crm_free(rsc->allocated_to); - rsc->allocated_to = NULL; - } + if(node == NULL && score == -INFINITY) { + if(rsc->allocated_to) { + crm_info("Deallocating %s from %s", rsc->id, rsc->allocated_to->details->uname); + crm_free(rsc->allocated_to); + rsc->allocated_to = NULL; } + } } #define sort_return(an_int) crm_free(a_uuid); crm_free(b_uuid); return an_int gint sort_op_by_callid(gconstpointer a, gconstpointer b) { - char *a_uuid = NULL; - char *b_uuid = NULL; - const xmlNode *xml_a = a; - const xmlNode *xml_b = b; + char *a_uuid = NULL; + char *b_uuid = NULL; + const xmlNode *xml_a = a; + const xmlNode *xml_b = b; - const char *a_xml_id = crm_element_value_const(xml_a, XML_ATTR_ID); - const char *b_xml_id = crm_element_value_const(xml_b, XML_ATTR_ID); + const char *a_xml_id = crm_element_value_const(xml_a, XML_ATTR_ID); + const char *b_xml_id = crm_element_value_const(xml_b, XML_ATTR_ID); - const char *a_task_id = crm_element_value_const(xml_a, XML_LRM_ATTR_CALLID); - const char *b_task_id = crm_element_value_const(xml_b, XML_LRM_ATTR_CALLID); + const char *a_task_id = crm_element_value_const(xml_a, XML_LRM_ATTR_CALLID); + const char *b_task_id = crm_element_value_const(xml_b, XML_LRM_ATTR_CALLID); - const char *a_key = crm_element_value_const(xml_a, XML_ATTR_TRANSITION_MAGIC); - const char *b_key = crm_element_value_const(xml_b, XML_ATTR_TRANSITION_MAGIC); + const char *a_key = crm_element_value_const(xml_a, XML_ATTR_TRANSITION_MAGIC); + const char *b_key = crm_element_value_const(xml_b, XML_ATTR_TRANSITION_MAGIC); - int dummy = -1; + int dummy = -1; - int a_id = -1; - int b_id = -1; + int a_id = -1; + int b_id = -1; - int a_rc = -1; - int b_rc = -1; + int a_rc = -1; + int b_rc = -1; - int a_status = -1; - int b_status = -1; + int a_status = -1; + int b_status = -1; - int a_call_id = -1; - int b_call_id = -1; - - if(safe_str_eq(a_xml_id, b_xml_id)) { - /* We have duplicate lrm_rsc_op entries in the status - * section which is unliklely to be a good thing - * - we can handle it easily enough, but we need to get - * to the bottom of why its happening. - */ - pe_err("Duplicate lrm_rsc_op entries named %s", a_xml_id); - sort_return(0); - } + int a_call_id = -1; + int b_call_id = -1; + + if(safe_str_eq(a_xml_id, b_xml_id)) { + /* We have duplicate lrm_rsc_op entries in the status + * section which is unliklely to be a good thing + * - we can handle it easily enough, but we need to get + * to the bottom of why its happening. + */ + pe_err("Duplicate lrm_rsc_op entries named %s", a_xml_id); + sort_return(0); + } - CRM_CHECK(a_task_id != NULL && b_task_id != NULL, - crm_err("a: %s, b: %s", crm_str(a_xml_id), crm_str(b_xml_id)); - sort_return(0)); - a_call_id = crm_parse_int(a_task_id, NULL); - b_call_id = crm_parse_int(b_task_id, NULL); + CRM_CHECK(a_task_id != NULL && b_task_id != NULL, + crm_err("a: %s, b: %s", crm_str(a_xml_id), crm_str(b_xml_id)); + sort_return(0)); + a_call_id = crm_parse_int(a_task_id, NULL); + b_call_id = crm_parse_int(b_task_id, NULL); - if(a_call_id == -1 && b_call_id == -1) { - /* both are pending ops so it doesnt matter since - * stops are never pending - */ - sort_return(0); - - } else if(a_call_id >= 0 && a_call_id < b_call_id) { - crm_debug_4("%s (%d) < %s (%d) : call id", - a_xml_id, a_call_id, b_xml_id, b_call_id); - sort_return(-1); - - } else if(b_call_id >= 0 && a_call_id > b_call_id) { - crm_debug_4("%s (%d) > %s (%d) : call id", - a_xml_id, a_call_id, b_xml_id, b_call_id); - sort_return(1); - } + if(a_call_id == -1 && b_call_id == -1) { + /* both are pending ops so it doesnt matter since + * stops are never pending + */ + sort_return(0); - crm_debug_5("%s (%d) == %s (%d) : continuing", + } else if(a_call_id >= 0 && a_call_id < b_call_id) { + crm_debug_4("%s (%d) < %s (%d) : call id", a_xml_id, a_call_id, b_xml_id, b_call_id); - - /* now process pending ops */ - CRM_CHECK(a_key != NULL && b_key != NULL, sort_return(0)); - CRM_CHECK(decode_transition_magic( - a_key, &a_uuid, &a_id, &dummy, &a_status, &a_rc, &dummy), - sort_return(0)); - CRM_CHECK(decode_transition_magic( - b_key, &b_uuid, &b_id, &dummy, &b_status, &b_rc, &dummy), - sort_return(0)); + sort_return(-1); - /* try and determin the relative age of the operation... - * some pending operations (ie. a start) may have been supuerceeded - * by a subsequent stop + } else if(b_call_id >= 0 && a_call_id > b_call_id) { + crm_debug_4("%s (%d) > %s (%d) : call id", + a_xml_id, a_call_id, b_xml_id, b_call_id); + sort_return(1); + } + + crm_debug_5("%s (%d) == %s (%d) : continuing", + a_xml_id, a_call_id, b_xml_id, b_call_id); + + /* now process pending ops */ + CRM_CHECK(a_key != NULL && b_key != NULL, sort_return(0)); + CRM_CHECK(decode_transition_magic( + a_key, &a_uuid, &a_id, &dummy, &a_status, &a_rc, &dummy), + sort_return(0)); + CRM_CHECK(decode_transition_magic( + b_key, &b_uuid, &b_id, &dummy, &b_status, &b_rc, &dummy), + sort_return(0)); + + /* try and determin the relative age of the operation... + * some pending operations (ie. a start) may have been supuerceeded + * by a subsequent stop + * + * [a|b]_id == -1 means its a shutdown operation and _always_ comes last + */ + if(safe_str_neq(a_uuid, b_uuid) || a_id == b_id) { + /* + * some of the logic in here may be redundant... * - * [a|b]_id == -1 means its a shutdown operation and _always_ comes last + * if the UUID from the TE doesnt match then one better + * be a pending operation. + * pending operations dont survive between elections and joins + * because we query the LRM directly */ - if(safe_str_neq(a_uuid, b_uuid) || a_id == b_id) { - /* - * some of the logic in here may be redundant... - * - * if the UUID from the TE doesnt match then one better - * be a pending operation. - * pending operations dont survive between elections and joins - * because we query the LRM directly - */ - - CRM_CHECK(a_call_id == -1 || b_call_id == -1, - crm_err("a: %s=%d, b: %s=%d", - crm_str(a_xml_id), a_call_id, crm_str(b_xml_id), b_call_id); - sort_return(0)); - CRM_CHECK(a_call_id >= 0 || b_call_id >= 0, sort_return(0)); - - if(b_call_id == -1) { - crm_debug_2("%s (%d) < %s (%d) : transition + call id", - a_xml_id, a_call_id, b_xml_id, b_call_id); - sort_return(-1); - } - - if(a_call_id == -1) { - crm_debug_2("%s (%d) > %s (%d) : transition + call id", - a_xml_id, a_call_id, b_xml_id, b_call_id); - sort_return(1); - } - } else if((a_id >= 0 && a_id < b_id) || b_id == -1) { - crm_debug_3("%s (%d) < %s (%d) : transition", - a_xml_id, a_id, b_xml_id, b_id); - sort_return(-1); - - } else if((b_id >= 0 && a_id > b_id) || a_id == -1) { - crm_debug_3("%s (%d) > %s (%d) : transition", - a_xml_id, a_id, b_xml_id, b_id); - sort_return(1); + CRM_CHECK(a_call_id == -1 || b_call_id == -1, + crm_err("a: %s=%d, b: %s=%d", + crm_str(a_xml_id), a_call_id, crm_str(b_xml_id), b_call_id); + sort_return(0)); + CRM_CHECK(a_call_id >= 0 || b_call_id >= 0, sort_return(0)); + + if(b_call_id == -1) { + crm_debug_2("%s (%d) < %s (%d) : transition + call id", + a_xml_id, a_call_id, b_xml_id, b_call_id); + sort_return(-1); + } + + if(a_call_id == -1) { + crm_debug_2("%s (%d) > %s (%d) : transition + call id", + a_xml_id, a_call_id, b_xml_id, b_call_id); + sort_return(1); } + + } else if((a_id >= 0 && a_id < b_id) || b_id == -1) { + crm_debug_3("%s (%d) < %s (%d) : transition", + a_xml_id, a_id, b_xml_id, b_id); + sort_return(-1); + + } else if((b_id >= 0 && a_id > b_id) || a_id == -1) { + crm_debug_3("%s (%d) > %s (%d) : transition", + a_xml_id, a_id, b_xml_id, b_id); + sort_return(1); + } - /* we should never end up here */ - crm_err("%s (%d:%d:%s) ?? %s (%d:%d:%s) : default", - a_xml_id, a_call_id, a_id, a_uuid, b_xml_id, b_call_id, b_id, b_uuid); - CRM_CHECK(FALSE, sort_return(0)); + /* we should never end up here */ + crm_err("%s (%d:%d:%s) ?? %s (%d:%d:%s) : default", + a_xml_id, a_call_id, a_id, a_uuid, b_xml_id, b_call_id, b_id, b_uuid); + CRM_CHECK(FALSE, sort_return(0)); } time_t get_timet_now(pe_working_set_t *data_set) { time_t now = 0; if(data_set && data_set->now) { now = data_set->now->tm_now; } if(now == 0) { /* eventually we should convert data_set->now into time_tm * for now, its only triggered by PE regression tests */ now = time(NULL); crm_crit("Defaulting to 'now'"); if(data_set && data_set->now) { data_set->now->tm_now = now; } } return now; } struct fail_search { resource_t *rsc; int count; long long last; char *key; }; static void get_failcount_by_prefix(gpointer key_p, gpointer value, gpointer user_data) { struct fail_search *search = user_data; const char *key = key_p; const char *match = strstr(key, search->key); if(match) { if(strstr(key, "last-failure-") == key && (key+13) == match) { search->last = crm_int_helper(value, NULL); } else if(strstr(key, "fail-count-") == key && (key+11) == match) { search->count += char2score(value); } } } int get_failcount(node_t *node, resource_t *rsc, int *last_failure, pe_working_set_t *data_set) { struct fail_search search = {rsc, 0, 0, NULL}; search.key = crm_strdup(rsc->id); if(is_not_set(rsc->flags, pe_rsc_unique)) { int lpc = 0; search.rsc = uber_parent(rsc); /* Strip the clone incarnation */ for(lpc = strlen(search.key); lpc > 0; lpc--) { if(search.key[lpc] == ':') { search.key[lpc+1] = 0; break; } } g_hash_table_foreach(node->details->attrs, get_failcount_by_prefix, &search); } else { /* Optimize the "normal" case */ char *key = NULL; const char *value = NULL; key = crm_concat("fail-count", rsc->id, '-'); value = g_hash_table_lookup(node->details->attrs, key); search.count = char2score(value); crm_free(key); key = crm_concat("last-failure", rsc->id, '-'); value = g_hash_table_lookup(node->details->attrs, key); search.last = crm_int_helper(value, NULL); crm_free(key); } if(search.count != 0 && search.last != 0 && rsc->failure_timeout) { if(last_failure) { *last_failure = search.last; } if(search.last > 0) { time_t now = get_timet_now(data_set); if(now > (search.last + rsc->failure_timeout)) { crm_notice("Failcount for %s on %s has expired (limit was %ds)", search.rsc->id, node->details->uname, rsc->failure_timeout); search.count = 0; } } } if(search.count != 0) { crm_info("%s has failed %s times on %s", search.rsc->id, score2char(search.count), node->details->uname); } crm_free(search.key); return search.count; } gboolean get_target_role(resource_t *rsc, enum rsc_role_e *role) { const char *value = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE); CRM_CHECK(role != NULL, return FALSE); if(value == NULL || safe_str_eq("started", value) || safe_str_eq("default", value)) { return FALSE; } *role = text2role(value); if(*role == RSC_ROLE_UNKNOWN) { crm_config_err("%s: Unknown value for %s: %s", rsc->id, XML_RSC_ATTR_TARGET_ROLE, value); return FALSE; } else if(*role > RSC_ROLE_STARTED) { const char *stateful = g_hash_table_lookup(rsc->meta, "stateful"); if(rsc->variant == pe_master) { /* Next check isn't true during common_unpack() for the master */ } else if(crm_is_true(stateful) == FALSE) { pe_warn("%s is not part of a master/slave resource, a %s of '%s' makes no sense: %s", rsc->id, XML_RSC_ATTR_TARGET_ROLE, value, stateful); *role = RSC_ROLE_STARTED; return FALSE; } } return TRUE; } diff --git a/lib/transition/unpack.c b/lib/transition/unpack.c index 8fde9d4cc8..f836601bd5 100644 --- a/lib/transition/unpack.c +++ b/lib/transition/unpack.c @@ -1,323 +1,323 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include CRM_TRACE_INIT_DATA(transitioner); static crm_action_t* unpack_action(synapse_t *parent, xmlNode *xml_action) { - crm_action_t *action = NULL; - xmlNode *action_copy = NULL; - const char *value = crm_element_value(xml_action, XML_ATTR_ID); - - if(value == NULL) { - crm_err("Actions must have an id!"); - crm_log_xml_debug_3(xml_action, "Action with missing id"); - return NULL; - } + crm_action_t *action = NULL; + xmlNode *action_copy = NULL; + const char *value = crm_element_value(xml_action, XML_ATTR_ID); + + if(value == NULL) { + crm_err("Actions must have an id!"); + crm_log_xml_debug_3(xml_action, "Action with missing id"); + return NULL; + } - action_copy = copy_xml(xml_action); - crm_malloc0(action, sizeof(crm_action_t)); - if(action == NULL) { - return NULL; - } + action_copy = copy_xml(xml_action); + crm_malloc0(action, sizeof(crm_action_t)); + if(action == NULL) { + return NULL; + } - action->id = crm_parse_int(value, NULL); - action->type = action_type_rsc; - action->xml = action_copy; - action->synapse = parent; + action->id = crm_parse_int(value, NULL); + action->type = action_type_rsc; + action->xml = action_copy; + action->synapse = parent; - if(safe_str_eq(crm_element_name(action_copy), XML_GRAPH_TAG_RSC_OP)) { - action->type = action_type_rsc; + if(safe_str_eq(crm_element_name(action_copy), XML_GRAPH_TAG_RSC_OP)) { + action->type = action_type_rsc; - } else if(safe_str_eq(crm_element_name(action_copy), - XML_GRAPH_TAG_PSEUDO_EVENT)) { - action->type = action_type_pseudo; + } else if(safe_str_eq(crm_element_name(action_copy), + XML_GRAPH_TAG_PSEUDO_EVENT)) { + action->type = action_type_pseudo; - } else if(safe_str_eq(crm_element_name(action_copy), - XML_GRAPH_TAG_CRM_EVENT)) { - action->type = action_type_crm; - } + } else if(safe_str_eq(crm_element_name(action_copy), + XML_GRAPH_TAG_CRM_EVENT)) { + action->type = action_type_crm; + } - action->params = xml2list(action_copy); + action->params = xml2list(action_copy); - value = g_hash_table_lookup(action->params, "CRM_meta_timeout"); - if(value != NULL) { - action->timeout = crm_parse_int(value, NULL); - } + value = g_hash_table_lookup(action->params, "CRM_meta_timeout"); + if(value != NULL) { + action->timeout = crm_parse_int(value, NULL); + } - value = g_hash_table_lookup(action->params, "CRM_meta_interval"); - if(value != NULL) { - action->interval = crm_parse_int(value, NULL); - } + value = g_hash_table_lookup(action->params, "CRM_meta_interval"); + if(value != NULL) { + action->interval = crm_parse_int(value, NULL); + } - value = g_hash_table_lookup(action->params, "CRM_meta_can_fail"); - if(value != NULL) { - crm_str_to_boolean(value, &(action->can_fail)); - } + value = g_hash_table_lookup(action->params, "CRM_meta_can_fail"); + if(value != NULL) { + crm_str_to_boolean(value, &(action->can_fail)); + } - crm_debug_3("Action %d has timer set to %dms", - action->id, action->timeout); + crm_debug_3("Action %d has timer set to %dms", + action->id, action->timeout); - return action; + return action; } static synapse_t * unpack_synapse(crm_graph_t *new_graph, xmlNode *xml_synapse) { - const char *value = NULL; - synapse_t *new_synapse = NULL; - CRM_CHECK(xml_synapse != NULL, return NULL); - crm_debug_3("looking in synapse %s", ID(xml_synapse)); + const char *value = NULL; + xmlNode *inputs = NULL; + xmlNode *action_set = NULL; + synapse_t *new_synapse = NULL; + CRM_CHECK(xml_synapse != NULL, return NULL); + crm_debug_3("looking in synapse %s", ID(xml_synapse)); - crm_malloc0(new_synapse, sizeof(synapse_t)); - new_synapse->id = crm_parse_int(ID(xml_synapse), NULL); + crm_malloc0(new_synapse, sizeof(synapse_t)); + new_synapse->id = crm_parse_int(ID(xml_synapse), NULL); - value = crm_element_value(xml_synapse, XML_CIB_ATTR_PRIORITY); - if(value != NULL) { - new_synapse->priority = crm_parse_int(value, NULL); - } + value = crm_element_value(xml_synapse, XML_CIB_ATTR_PRIORITY); + if(value != NULL) { + new_synapse->priority = crm_parse_int(value, NULL); + } - new_graph->num_synapses++; - CRM_CHECK(new_synapse->id >= 0, crm_free(new_synapse); return NULL); + new_graph->num_synapses++; + CRM_CHECK(new_synapse->id >= 0, crm_free(new_synapse); return NULL); - crm_debug_3("look for actions in synapse %s", - crm_element_value(xml_synapse, XML_ATTR_ID)); + crm_debug_3("look for actions in synapse %s", + crm_element_value(xml_synapse, XML_ATTR_ID)); - xml_child_iter_filter( - xml_synapse, action_set, "action_set", - - xml_child_iter( - action_set, action, - - crm_action_t *new_action = unpack_action( - new_synapse, action); - new_graph->num_actions++; - - if(new_action == NULL) { - continue; - } - crm_debug_3("Adding action %d to synapse %d", - new_action->id, new_synapse->id); - - new_synapse->actions = g_list_append( - new_synapse->actions, new_action); - ); - - ); - - crm_debug_3("look for inputs in synapse %s", ID(xml_synapse)); + for(action_set = xml_synapse; action_set != NULL; action_set = action_set->next) { + if(crm_str_eq((const char *)action_set->name, "action_set", TRUE)) { + xmlNode *action = NULL; + for(action = action_set; action != NULL; action = action->next) { + crm_action_t *new_action = unpack_action(new_synapse, action); + new_graph->num_actions++; + + if(new_action == NULL) { + continue; + } + crm_debug_3("Adding action %d to synapse %d", + new_action->id, new_synapse->id); + + new_synapse->actions = g_list_append( + new_synapse->actions, new_action); + } + } + } - xml_child_iter_filter( - xml_synapse, inputs, "inputs", - - xml_child_iter( - inputs, trigger, - - xml_child_iter( - trigger, input, - - crm_action_t *new_input = unpack_action( - new_synapse, input); - - if(new_input == NULL) { - continue; - } - crm_debug_3("Adding input %d to synapse %d", - new_input->id, new_synapse->id); + crm_debug_3("look for inputs in synapse %s", ID(xml_synapse)); + + for(inputs = xml_synapse; inputs != NULL; inputs = inputs->next) { + if(crm_str_eq((const char *)inputs->name, "inputs", TRUE)) { + xmlNode *trigger = NULL; + for(trigger = inputs; trigger != NULL; trigger = trigger->next) { + xmlNode *input = NULL; + for(input = trigger; input != NULL; input = input->next) { + crm_action_t *new_input = unpack_action( + new_synapse, input); + + if(new_input == NULL) { + continue; + } + + crm_debug_3("Adding input %d to synapse %d", + new_input->id, new_synapse->id); - new_synapse->inputs = g_list_append( - new_synapse->inputs, new_input); - ); - ); - ); - return new_synapse; + new_synapse->inputs = g_list_append( + new_synapse->inputs, new_input); + } + } + } + } + + return new_synapse; } crm_graph_t * unpack_graph(xmlNode *xml_graph, const char *reference) { /* - + - - - + + id = -1; + new_graph->abort_priority = 0; + new_graph->network_delay = -1; + new_graph->transition_timeout = -1; + new_graph->stonith_timeout = -1; + new_graph->completion_action = tg_done; + + if(reference) { + new_graph->source = crm_strdup(reference); + } else { + new_graph->source = crm_strdup("unknown"); + } - new_graph->id = -1; - new_graph->abort_priority = 0; - new_graph->network_delay = -1; - new_graph->transition_timeout = -1; - new_graph->stonith_timeout = -1; - new_graph->completion_action = tg_done; - - if(reference) { - new_graph->source = crm_strdup(reference); + if(xml_graph != NULL) { + t_id = crm_element_value(xml_graph, "transition_id"); + CRM_CHECK(t_id != NULL, crm_free(new_graph); return NULL); + new_graph->id = crm_parse_int(t_id, "-1"); + + time = crm_element_value(xml_graph, "cluster-delay"); + CRM_CHECK(time != NULL, crm_free(new_graph); return NULL); + new_graph->network_delay = crm_get_msec(time); + + time = crm_element_value(xml_graph, "stonith-timeout"); + if(time == NULL) { + new_graph->stonith_timeout = new_graph->network_delay; } else { - new_graph->source = crm_strdup("unknown"); + new_graph->stonith_timeout = crm_get_msec(time); } - - if(xml_graph != NULL) { - t_id = crm_element_value(xml_graph, "transition_id"); - CRM_CHECK(t_id != NULL, crm_free(new_graph); return NULL); - new_graph->id = crm_parse_int(t_id, "-1"); - - time = crm_element_value(xml_graph, "cluster-delay"); - CRM_CHECK(time != NULL, crm_free(new_graph); return NULL); - new_graph->network_delay = crm_get_msec(time); - - time = crm_element_value(xml_graph, "stonith-timeout"); - if(time == NULL) { - new_graph->stonith_timeout = new_graph->network_delay; - } else { - new_graph->stonith_timeout = crm_get_msec(time); - } - t_id = crm_element_value(xml_graph, "batch-limit"); - new_graph->batch_limit = crm_parse_int(t_id, "0"); - } + t_id = crm_element_value(xml_graph, "batch-limit"); + new_graph->batch_limit = crm_parse_int(t_id, "0"); + } - xml_child_iter_filter( - xml_graph, synapse, "synapse", - - synapse_t *new_synapse = unpack_synapse(new_graph, synapse); - if(new_synapse != NULL) { - new_graph->synapses = g_list_append( - new_graph->synapses, new_synapse); - } - ); + for(synapse = xml_graph; synapse != NULL; synapse = synapse->next) { + if(crm_str_eq((const char *)synapse->name, "synapse", TRUE)) { + synapse_t *new_synapse = unpack_synapse(new_graph, synapse); + if(new_synapse != NULL) { + new_graph->synapses = g_list_append( + new_graph->synapses, new_synapse); + } + } + } - crm_info("Unpacked transition %d: %d actions in %d synapses", - new_graph->id, new_graph->num_actions,new_graph->num_synapses); + crm_info("Unpacked transition %d: %d actions in %d synapses", + new_graph->id, new_graph->num_actions,new_graph->num_synapses); - return new_graph; + return new_graph; } static void destroy_action(crm_action_t *action) { - if(action->timer && action->timer->source_id != 0) { - crm_warn("Cancelling timer for action %d (src=%d)", - action->id, action->timer->source_id); - g_source_remove(action->timer->source_id); - } - if(action->params) { - g_hash_table_destroy(action->params); - } - free_xml(action->xml); - crm_free(action->timer); - crm_free(action); + if(action->timer && action->timer->source_id != 0) { + crm_warn("Cancelling timer for action %d (src=%d)", + action->id, action->timer->source_id); + g_source_remove(action->timer->source_id); + } + if(action->params) { + g_hash_table_destroy(action->params); + } + free_xml(action->xml); + crm_free(action->timer); + crm_free(action); } static void destroy_synapse(synapse_t *synapse) { - while(g_list_length(synapse->actions) > 0) { - crm_action_t *action = g_list_nth_data(synapse->actions, 0); - synapse->actions = g_list_remove(synapse->actions, action); - destroy_action(action); - } + while(g_list_length(synapse->actions) > 0) { + crm_action_t *action = g_list_nth_data(synapse->actions, 0); + synapse->actions = g_list_remove(synapse->actions, action); + destroy_action(action); + } - while(g_list_length(synapse->inputs) > 0) { - crm_action_t *action = g_list_nth_data(synapse->inputs, 0); - synapse->inputs = g_list_remove(synapse->inputs, action); - destroy_action(action); - } - crm_free(synapse); + while(g_list_length(synapse->inputs) > 0) { + crm_action_t *action = g_list_nth_data(synapse->inputs, 0); + synapse->inputs = g_list_remove(synapse->inputs, action); + destroy_action(action); + } + crm_free(synapse); } void destroy_graph(crm_graph_t *graph) { - if(graph == NULL) { - return; - } - while(g_list_length(graph->synapses) > 0) { - synapse_t *synapse = g_list_nth_data(graph->synapses, 0); - graph->synapses = g_list_remove(graph->synapses, synapse); - destroy_synapse(synapse); - } - crm_free(graph->source); - crm_free(graph); + if(graph == NULL) { + return; + } + while(g_list_length(graph->synapses) > 0) { + synapse_t *synapse = g_list_nth_data(graph->synapses, 0); + graph->synapses = g_list_remove(graph->synapses, synapse); + destroy_synapse(synapse); + } + crm_free(graph->source); + crm_free(graph); } lrm_op_t *convert_graph_action(xmlNode *resource, crm_action_t *action, int status, int rc) { + xmlNode *xop = NULL; lrm_op_t *op = NULL; GHashTableIter iter; const char *name = NULL; const char *value = NULL; xmlNode *action_resource = NULL; CRM_CHECK(action != NULL, return NULL); CRM_CHECK(action->type == action_type_rsc, return NULL); action_resource = first_named_child(action->xml, XML_CIB_TAG_RESOURCE); CRM_CHECK(action_resource != NULL, crm_log_xml_warn(action->xml, "Bad"); return NULL); crm_malloc0(op, sizeof(lrm_op_t)); op->app_name = crm_strdup(crm_system_name); op->rsc_id = crm_strdup(ID(action_resource)); op->interval = action->interval; op->op_type = crm_strdup(crm_element_value(action->xml, XML_LRM_ATTR_TASK)); op->rc = rc; op->op_status = status; op->params = g_hash_table_new_full(g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); g_hash_table_iter_init (&iter, action->params); while (g_hash_table_iter_next (&iter, (void**)&name, (void**)&value)) { g_hash_table_insert(op->params, crm_strdup(name), crm_strdup(value)); } - op->call_id = 0; - xml_child_iter(resource, xop, - int tmp = 0; - crm_element_value_int(xop, XML_LRM_ATTR_CALLID, &tmp); - crm_info("Got call_id=%d for %s", tmp, ID(resource)); - if(tmp > op->call_id) { - op->call_id = tmp; - } - ); + for(xop = resource; xop != NULL; xop = xop->next) { + int tmp = 0; + crm_element_value_int(xop, XML_LRM_ATTR_CALLID, &tmp); + crm_info("Got call_id=%d for %s", tmp, ID(resource)); + if(tmp > op->call_id) { + op->call_id = tmp; + } + } op->call_id++; return op; } diff --git a/pengine/allocate.c b/pengine/allocate.c index 962a00be1f..8268311c40 100644 --- a/pengine/allocate.c +++ b/pengine/allocate.c @@ -1,2026 +1,2032 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include CRM_TRACE_INIT_DATA(pe_allocate); 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[] = { { rsc_merge_weights, native_color, native_create_actions, native_create_probe, native_internal_constraints, native_rsc_colocation_lh, native_rsc_colocation_rh, native_rsc_location, native_action_flags, native_update_actions, native_expand, native_append_meta, }, { group_merge_weights, group_color, group_create_actions, native_create_probe, group_internal_constraints, group_rsc_colocation_lh, group_rsc_colocation_rh, group_rsc_location, group_action_flags, group_update_actions, group_expand, group_append_meta, }, { rsc_merge_weights, clone_color, clone_create_actions, clone_create_probe, clone_internal_constraints, clone_rsc_colocation_lh, clone_rsc_colocation_rh, clone_rsc_location, clone_action_flags, clone_update_actions, clone_expand, clone_append_meta, }, { rsc_merge_weights, master_color, master_create_actions, clone_create_probe, master_internal_constraints, clone_rsc_colocation_lh, master_rsc_colocation_rh, clone_rsc_location, clone_action_flags, clone_update_actions, clone_expand, master_append_meta, } }; static gboolean check_rsc_parameters(resource_t *rsc, node_t *node, xmlNode *rsc_entry, pe_working_set_t *data_set) { int attr_lpc = 0; gboolean force_restart = FALSE; gboolean delete_resource = FALSE; const char *value = NULL; const char *old_value = NULL; const char *attr_list[] = { XML_ATTR_TYPE, XML_AGENT_ATTR_CLASS, XML_AGENT_ATTR_PROVIDER }; for(; attr_lpc < DIMOF(attr_list); attr_lpc++) { value = crm_element_value(rsc->xml, attr_list[attr_lpc]); old_value = crm_element_value(rsc_entry, attr_list[attr_lpc]); if(value == old_value /* ie. NULL */ || crm_str_eq(value, old_value, TRUE)) { continue; } force_restart = TRUE; crm_notice("Forcing restart of %s on %s, %s changed: %s -> %s", rsc->id, node->details->uname, attr_list[attr_lpc], crm_str(old_value), crm_str(value)); } if(force_restart) { /* make sure the restart happens */ stop_action(rsc, node, FALSE); set_bit(rsc->flags, pe_rsc_start_pending); delete_resource = TRUE; } return delete_resource; } static void CancelXmlOp(resource_t *rsc, xmlNode *xml_op, node_t *active_node, const char *reason, pe_working_set_t *data_set) { int interval = 0; action_t *cancel = NULL; char *key = NULL; const char *task = NULL; const char *call_id = NULL; const char *interval_s = NULL; CRM_CHECK(xml_op != NULL, return); CRM_CHECK(active_node != NULL, return); task = crm_element_value(xml_op, XML_LRM_ATTR_TASK); call_id = crm_element_value(xml_op, XML_LRM_ATTR_CALLID); interval_s = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL); interval = crm_parse_int(interval_s, "0"); /* we need to reconstruct the key because of the way we used to construct resource IDs */ key = generate_op_key(rsc->id, task, interval); crm_info("Action %s on %s will be stopped: %s", key, active_node->details->uname, reason?reason:"unknown"); cancel = custom_action(rsc, crm_strdup(key), RSC_CANCEL, active_node, FALSE, TRUE, data_set); crm_free(cancel->task); cancel->task = crm_strdup(RSC_CANCEL); add_hash_param(cancel->meta, XML_LRM_ATTR_TASK, task); add_hash_param(cancel->meta, XML_LRM_ATTR_CALLID, call_id); add_hash_param(cancel->meta, XML_LRM_ATTR_INTERVAL, interval_s); custom_action_order(rsc, stop_key(rsc), NULL, rsc, NULL, cancel, pe_order_optional, data_set); crm_free(key); key = NULL; } static gboolean check_action_definition(resource_t *rsc, node_t *active_node, xmlNode *xml_op, pe_working_set_t *data_set) { char *key = NULL; int interval = 0; const char *interval_s = NULL; gboolean did_change = FALSE; gboolean start_op = FALSE; xmlNode *params_all = NULL; xmlNode *params_restart = NULL; GHashTable *local_rsc_params = NULL; char *digest_all_calc = NULL; const char *digest_all = NULL; const char *restart_list = NULL; const char *digest_restart = NULL; char *digest_restart_calc = NULL; action_t *action = NULL; const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK); const char *op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION); CRM_CHECK(active_node != NULL, return FALSE); interval_s = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL); interval = crm_parse_int(interval_s, "0"); /* we need to reconstruct the key because of the way we used to construct resource IDs */ key = generate_op_key(rsc->id, task, interval); if(interval > 0) { xmlNode *op_match = NULL; crm_debug_2("Checking parameters for %s", key); op_match = find_rsc_op_entry(rsc, key); if(op_match == NULL && is_set(data_set->flags, pe_flag_stop_action_orphans)) { CancelXmlOp(rsc, xml_op, active_node, "orphan", data_set); crm_free(key); key = NULL; return TRUE; } else if(op_match == NULL) { crm_debug("Orphan action detected: %s on %s", key, active_node->details->uname); crm_free(key); key = NULL; return TRUE; } } action = custom_action(rsc, key, task, active_node, TRUE, FALSE, data_set); local_rsc_params = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); get_rsc_attributes(local_rsc_params, rsc, active_node, data_set); params_all = create_xml_node(NULL, XML_TAG_PARAMS); g_hash_table_foreach(local_rsc_params, hash2field, params_all); g_hash_table_foreach(action->extra, hash2field, params_all); g_hash_table_foreach(rsc->parameters, hash2field, params_all); g_hash_table_foreach(action->meta, hash2metafield, params_all); filter_action_parameters(params_all, op_version); digest_all_calc = calculate_operation_digest(params_all, op_version); digest_all = crm_element_value(xml_op, XML_LRM_ATTR_OP_DIGEST); digest_restart = crm_element_value(xml_op, XML_LRM_ATTR_RESTART_DIGEST); restart_list = crm_element_value(xml_op, XML_LRM_ATTR_OP_RESTART); if(crm_str_eq(task, RSC_START, TRUE)) { start_op = TRUE; } if(start_op && digest_restart) { params_restart = copy_xml(params_all); if(restart_list) { filter_reload_parameters(params_restart, restart_list); } digest_restart_calc = calculate_operation_digest(params_restart, op_version); if(safe_str_neq(digest_restart_calc, digest_restart)) { did_change = TRUE; crm_log_xml_info(params_restart, "params:restart"); crm_warn("Parameters to %s on %s changed: recorded %s vs. %s (restart:%s) %s", key, active_node->details->uname, crm_str(digest_restart), digest_restart_calc, op_version, crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC)); key = generate_op_key(rsc->id, task, interval); custom_action(rsc, key, task, NULL, FALSE, TRUE, data_set); goto cleanup; } } if(safe_str_neq(digest_all_calc, digest_all)) { action_t *op = NULL; did_change = TRUE; crm_log_xml_info(params_all, "params:all"); crm_warn("Parameters to %s on %s changed: recorded %s vs. %s (all:%s) %s", key, active_node->details->uname, crm_str(digest_all), digest_all_calc, op_version, crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC)); if(interval == 0 && safe_str_neq(task, RSC_STOP)) { /* Anything except stop actions should result in a restart, * never a re-probe */ task = RSC_START; } key = generate_op_key(rsc->id, task, interval); op = custom_action(rsc, key, task, NULL, FALSE, TRUE, data_set); if(start_op && digest_restart) { update_action_flags(op, pe_action_allow_reload_conversion); } else if(interval > 0) { custom_action_order(rsc, start_key(rsc), NULL, NULL, crm_strdup(op->task), op, pe_order_runnable_left, data_set); } } cleanup: free_xml(params_all); free_xml(params_restart); crm_free(digest_all_calc); crm_free(digest_restart_calc); g_hash_table_destroy(local_rsc_params); pe_free_action(action); return did_change; } extern gboolean DeleteRsc(resource_t *rsc, node_t *node, gboolean optional, pe_working_set_t *data_set); static void check_actions_for(xmlNode *rsc_entry, resource_t *rsc, node_t *node, pe_working_set_t *data_set) { GListPtr gIter = NULL; int offset = -1; int interval = 0; int stop_index = 0; int start_index = 0; const char *task = NULL; const char *interval_s = NULL; + xmlNode *rsc_op = NULL; GListPtr op_list = NULL; GListPtr sorted_op_list = NULL; gboolean is_probe = FALSE; CRM_CHECK(node != NULL, return); if(is_set(rsc->flags, pe_rsc_orphan)) { crm_debug_2("Skipping param check for %s: orphan", rsc->id); return; } else if(pe_find_node_id(rsc->running_on, node->details->id) == NULL) { crm_debug_2("Skipping param check for %s: no longer active on %s", rsc->id, node->details->uname); return; } crm_debug_3("Processing %s on %s", rsc->id, node->details->uname); if(check_rsc_parameters(rsc, node, rsc_entry, data_set)) { DeleteRsc(rsc, node, FALSE, data_set); } - xml_child_iter_filter( - rsc_entry, rsc_op, XML_LRM_TAG_RSC_OP, - op_list = g_list_prepend(op_list, rsc_op); - ); + for(rsc_op = rsc_entry; rsc_op != NULL; rsc_op = rsc_op->next) { + if(crm_str_eq((const char *)rsc_op->name, XML_LRM_TAG_RSC_OP, TRUE)) { + op_list = g_list_prepend(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); gIter = sorted_op_list; for(; gIter != NULL; gIter = gIter->next) { xmlNode *rsc_op = (xmlNode*)gIter->data; offset++; if(start_index < stop_index) { /* stopped */ continue; } else if(offset < start_index) { /* action occurred prior to a start */ continue; } is_probe = FALSE; task = crm_element_value(rsc_op, XML_LRM_ATTR_TASK); interval_s = crm_element_value(rsc_op, XML_LRM_ATTR_INTERVAL); interval = crm_parse_int(interval_s, "0"); if(interval == 0 && safe_str_eq(task, RSC_STATUS)) { is_probe = TRUE; } if(interval > 0 && is_set(data_set->flags, pe_flag_maintenance_mode)) { CancelXmlOp(rsc, rsc_op, node, "maintenance mode", data_set); } else if(is_probe || safe_str_eq(task, RSC_START) || interval > 0) { check_action_definition(rsc, node, rsc_op, data_set); } } g_list_free(sorted_op_list); } static GListPtr find_rsc_list( GListPtr result, resource_t *rsc, const char *id, gboolean renamed_clones, gboolean partial, pe_working_set_t *data_set) { GListPtr gIter = NULL; gboolean match = FALSE; if(id == NULL) { return NULL; } else if(rsc == NULL && data_set) { gIter = data_set->resources; for(; gIter != NULL; gIter = gIter->next) { resource_t *child = (resource_t*)gIter->data; result = find_rsc_list(result, child, id, renamed_clones, partial, NULL); } return result; } else if(rsc == NULL) { return NULL; } if(partial) { if(strstr(rsc->id, id)) { match = TRUE; } else if(rsc->long_name && strstr(rsc->long_name, id)) { match = TRUE; } else if(renamed_clones && rsc->clone_name && strstr(rsc->clone_name, id)) { match = TRUE; } } else { if(strcmp(rsc->id, id) == 0){ match = TRUE; } else if(rsc->long_name && strcmp(rsc->long_name, id) == 0) { match = TRUE; } else if(renamed_clones && rsc->clone_name && strcmp(rsc->clone_name, id) == 0) { match = TRUE; } } if(match) { result = g_list_prepend(result, rsc); } if(rsc->children) { gIter = rsc->children; for(; gIter != NULL; gIter = gIter->next) { resource_t *child = (resource_t*)gIter->data; result = find_rsc_list(result, child, id, renamed_clones, partial, NULL); } } return result; } static void check_actions(pe_working_set_t *data_set) { const char *id = NULL; node_t *node = NULL; xmlNode *lrm_rscs = NULL; xmlNode *status = get_object_root(XML_CIB_TAG_STATUS, data_set->input); - xml_child_iter_filter( - status, node_state, XML_CIB_TAG_STATE, - - id = crm_element_value(node_state, XML_ATTR_ID); - lrm_rscs = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE); - lrm_rscs = find_xml_node(lrm_rscs, XML_LRM_TAG_RESOURCES, FALSE); - - node = pe_find_node_id(data_set->nodes, id); - - if(node == NULL) { - continue; - - } else if(can_run_resources(node) == FALSE) { - crm_debug_2("Skipping param check for %s: cant run resources", - node->details->uname); - continue; - } + xmlNode *node_state = NULL; + for(node_state = status; node_state != NULL; node_state = node_state->next) { + if(crm_str_eq((const char *)node_state->name, XML_CIB_TAG_STATE, TRUE)) { + id = crm_element_value(node_state, XML_ATTR_ID); + lrm_rscs = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE); + lrm_rscs = find_xml_node(lrm_rscs, XML_LRM_TAG_RESOURCES, FALSE); + + node = pe_find_node_id(data_set->nodes, id); + + if(node == NULL) { + continue; + + } else if(can_run_resources(node) == FALSE) { + crm_debug_2("Skipping param check for %s: cant run resources", + node->details->uname); + continue; + } - crm_debug_2("Processing node %s", node->details->uname); - if(node->details->online || is_set(data_set->flags, pe_flag_stonith_enabled)) { - xml_child_iter_filter( - lrm_rscs, rsc_entry, XML_LRM_TAG_RESOURCE, - if(xml_has_children(rsc_entry)) { - GListPtr gIter = NULL; - GListPtr result = NULL; - const char *rsc_id = ID(rsc_entry); - CRM_CHECK(rsc_id != NULL, return); - - result = find_rsc_list(NULL, NULL, rsc_id, TRUE, FALSE, data_set); - gIter = result; - for(; gIter != NULL; gIter = gIter->next) { - resource_t *rsc = (resource_t*)gIter->data; + crm_debug_2("Processing node %s", node->details->uname); + if(node->details->online || is_set(data_set->flags, pe_flag_stonith_enabled)) { + xmlNode *rsc_entry = NULL; + for(rsc_entry = lrm_rscs; rsc_entry != NULL; rsc_entry = rsc_entry->next) { + if(crm_str_eq((const char *)rsc_entry->name, XML_LRM_TAG_RESOURCE, TRUE)) { + + if(xml_has_children(rsc_entry)) { + GListPtr gIter = NULL; + GListPtr result = NULL; + const char *rsc_id = ID(rsc_entry); + CRM_CHECK(rsc_id != NULL, return); + + result = find_rsc_list(NULL, NULL, rsc_id, TRUE, FALSE, data_set); + gIter = result; + for(; gIter != NULL; gIter = gIter->next) { + resource_t *rsc = (resource_t*)gIter->data; - check_actions_for(rsc_entry, rsc, node, data_set); + check_actions_for(rsc_entry, rsc, node, data_set); + } + g_list_free(result); + } } - g_list_free(result); } - ); + } } - ); + } } static gboolean apply_placement_constraints(pe_working_set_t *data_set) { GListPtr gIter = data_set->placement_constraints; crm_debug_3("Applying constraints..."); for(; gIter != NULL; gIter = gIter->next) { rsc_to_node_t *cons = (rsc_to_node_t*)gIter->data; cons->rsc_lh->cmds->rsc_location(cons->rsc_lh, cons); } return TRUE; } static void common_apply_stickiness(resource_t *rsc, node_t *node, pe_working_set_t *data_set) { int fail_count = 0; resource_t *failed = rsc; if(rsc->children) { GListPtr gIter = rsc->children; for(; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t*)gIter->data; common_apply_stickiness(child_rsc, node, data_set); } return; } if(is_set(rsc->flags, pe_rsc_managed) && rsc->stickiness != 0 && g_list_length(rsc->running_on) == 1) { node_t *current = pe_find_node_id(rsc->running_on, node->details->id); node_t *match = pe_hash_table_lookup(rsc->allowed_nodes, node->details->id); if(current == NULL) { } else if(match != NULL || is_set(data_set->flags, pe_flag_symmetric_cluster)) { resource_t *sticky_rsc = rsc; resource_location(sticky_rsc, node, rsc->stickiness, "stickiness", data_set); crm_debug("Resource %s: preferring current location" " (node=%s, weight=%d)", sticky_rsc->id, node->details->uname, rsc->stickiness); } else { GHashTableIter iter; node_t *node = NULL; crm_debug("Ignoring stickiness for %s: the cluster is asymmetric" " and node %s is not explicitly allowed", rsc->id, node->details->uname); g_hash_table_iter_init (&iter, rsc->allowed_nodes); while (g_hash_table_iter_next (&iter, NULL, (void**)&node)) { crm_err("%s[%s] = %d", rsc->id, node->details->uname, node->weight); } } } if(is_not_set(rsc->flags, pe_rsc_unique)) { failed = uber_parent(rsc); } fail_count = get_failcount(node, rsc, NULL, data_set); if(fail_count > 0 && rsc->migration_threshold != 0) { if(rsc->migration_threshold <= fail_count) { resource_location(failed, node, -INFINITY, "__fail_limit__", data_set); crm_warn("Forcing %s away from %s after %d failures (max=%d)", failed->id, node->details->uname, fail_count, rsc->migration_threshold); } else { crm_notice("%s can fail %d more times on %s before being forced off", failed->id, rsc->migration_threshold - fail_count, node->details->uname); } } } static void complex_set_cmds(resource_t *rsc) { GListPtr gIter = rsc->children; rsc->cmds = &resource_class_alloc_functions[rsc->variant]; for(; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t*)gIter->data; complex_set_cmds(child_rsc); } } void set_alloc_actions(pe_working_set_t *data_set) { GListPtr gIter = data_set->resources; for(; gIter != NULL; gIter = gIter->next) { resource_t *rsc = (resource_t*)gIter->data; complex_set_cmds(rsc); } } static void calculate_system_health (gpointer gKey, gpointer gValue, gpointer user_data) { const char *key = (const char *)gKey; const char *value = (const char *)gValue; int *system_health = (int *)user_data; if (!gKey || !gValue || !user_data) { return; } /* Does it start with #health? */ if (0 == strncmp (key, "#health", 7)) { int score; /* Convert the value into an integer */ score = char2score (value); /* Add it to the running total */ *system_health = merge_weights (score, *system_health); } } static gboolean apply_system_health(pe_working_set_t *data_set) { GListPtr gIter = NULL; const char *health_strategy = pe_pref(data_set->config_hash, "node-health-strategy"); if (health_strategy == NULL || safe_str_eq (health_strategy, "none")) { /* Prevent any accidental health -> score translation */ node_score_red = 0; node_score_yellow = 0; node_score_green = 0; return TRUE; } else if (safe_str_eq (health_strategy, "migrate-on-red")) { /* Resources on nodes which have health values of red are * weighted away from that node. */ node_score_red = -INFINITY; node_score_yellow = 0; node_score_green = 0; } else if (safe_str_eq (health_strategy, "only-green")) { /* Resources on nodes which have health values of red or yellow * are forced away from that node. */ node_score_red = -INFINITY; node_score_yellow = -INFINITY; node_score_green = 0; } else if (safe_str_eq (health_strategy, "progressive")) { /* Same as the above, but use the r/y/g scores provided by the user * Defaults are provided by the pe_prefs table */ } else if (safe_str_eq (health_strategy, "custom")) { /* Requires the admin to configure the rsc_location constaints for * processing the stored health scores */ /* TODO: Check for the existance of appropriate node health constraints */ return TRUE; } else { crm_err ("Unknown node health strategy: %s", health_strategy); return FALSE; } crm_info ("Applying automated node health strategy: %s", health_strategy); gIter = data_set->nodes; for(; gIter != NULL; gIter = gIter->next) { int system_health = 0; node_t *node = (node_t*)gIter->data; /* Search through the node hash table for system health entries. */ g_hash_table_foreach ( node->details->attrs, calculate_system_health, &system_health); crm_info (" Node %s has an combined system health of %d", node->details->uname, system_health); /* If the health is non-zero, then create a new rsc2node so that the * weight will be added later on. */ if (system_health != 0) { GListPtr gIter2 = data_set->resources; for(; gIter2 != NULL; gIter2 = gIter2->next) { resource_t *rsc = (resource_t*)gIter2->data; rsc2node_new (health_strategy, rsc, system_health, node, data_set); } } } return TRUE; } gboolean stage0(pe_working_set_t *data_set) { xmlNode * cib_constraints = get_object_root( XML_CIB_TAG_CONSTRAINTS, data_set->input); if(data_set->input == NULL) { return FALSE; } if(is_set(data_set->flags, pe_flag_have_status) == FALSE) { crm_trace("Calculating status"); cluster_status(data_set); } set_alloc_actions(data_set); apply_system_health(data_set); unpack_constraints(cib_constraints, data_set); return TRUE; } static void wait_for_probe( resource_t *rsc, const char *action, action_t *probe_complete, pe_working_set_t *data_set) { if(probe_complete == NULL) { return; } if(rsc->children) { GListPtr gIter = rsc->children; for(; gIter != NULL; gIter = gIter->next) { resource_t *child = (resource_t*)gIter->data; wait_for_probe(child, action, probe_complete, data_set); } } else { char *key = generate_op_key(rsc->id, action, 0); custom_action_order( NULL, NULL, probe_complete, rsc, key, NULL, pe_order_optional, data_set); } } /* * 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; GListPtr gIter = NULL; GListPtr gIter2 = NULL; gIter = data_set->nodes; for(; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t*)gIter->data; gboolean force_probe = FALSE; const char *probed = g_hash_table_lookup( node->details->attrs, CRM_OP_PROBED); if(node->details->online == FALSE) { continue; } else if(node->details->unclean) { continue; } else if(probe_complete == NULL) { probe_complete = get_pseudo_op(CRM_OP_PROBED, data_set); } if(probed != NULL && crm_is_true(probed) == FALSE) { force_probe = TRUE; } probe_node_complete = custom_action( NULL, crm_strdup(CRM_OP_PROBED), CRM_OP_PROBED, node, FALSE, TRUE, data_set); if(crm_is_true(probed)) { crm_trace("unset"); update_action_flags(probe_node_complete, pe_action_optional); } else { crm_trace("set"); update_action_flags(probe_node_complete, pe_action_optional|pe_action_clear); } crm_trace("%s - %d", node->details->uname, probe_node_complete->flags & pe_action_optional); probe_node_complete->priority = INFINITY; add_hash_param(probe_node_complete->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE); if(node->details->pending) { update_action_flags(probe_node_complete, pe_action_runnable|pe_action_clear); crm_info("Action %s on %s is unrunnable (pending)", probe_node_complete->uuid, probe_node_complete->node->details->uname); } order_actions(probe_node_complete, probe_complete, pe_order_runnable_left/*|pe_order_implies_then*/); gIter2 = data_set->resources; for(; gIter2 != NULL; gIter2 = gIter2->next) { resource_t *rsc = (resource_t*)gIter2->data; if(rsc->cmds->create_probe( rsc, node, probe_node_complete, force_probe, data_set)) { update_action_flags(probe_complete, pe_action_optional|pe_action_clear); update_action_flags(probe_node_complete, pe_action_optional|pe_action_clear); wait_for_probe(rsc, CRMD_ACTION_START, probe_complete, data_set); } } } gIter = data_set->resources; for(; gIter != NULL; gIter = gIter->next) { resource_t *rsc = (resource_t*)gIter->data; wait_for_probe(rsc, CRMD_ACTION_STOP, probe_complete, 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) { GListPtr gIter = NULL; crm_debug_3("Applying placement constraints"); gIter = data_set->nodes; for(; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t*)gIter->data; 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); gIter = data_set->nodes; for(; gIter != NULL; gIter = gIter->next) { GListPtr gIter2 = NULL; node_t *node = (node_t*)gIter->data; gIter2 = data_set->resources; for(; gIter2 != NULL; gIter2 = gIter2->next) { resource_t *rsc = (resource_t*)gIter2->data; common_apply_stickiness(rsc, node, data_set); } } return TRUE; } /* * Create internal resource constraints before allocation */ gboolean stage3(pe_working_set_t *data_set) { GListPtr gIter = data_set->resources; for(; gIter != NULL; gIter = gIter->next) { resource_t *rsc = (resource_t*)gIter->data; 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) { GListPtr gIter = NULL; gIter = data_set->nodes; for(; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t*)gIter->data; dump_node_capacity(show_utilization?0:utilization_log_level, "Original", node); } crm_trace("Allocating services"); /* Take (next) highest resource, assign it and create its actions */ gIter = data_set->resources; for(; gIter != NULL; gIter = gIter->next) { resource_t *rsc = (resource_t*)gIter->data; rsc->cmds->allocate(rsc, data_set); } gIter = data_set->nodes; for(; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t*)gIter->data; dump_node_capacity(show_utilization?0:utilization_log_level, "Remaining", node); } if(is_set(data_set->flags, pe_flag_startup_probes)) { crm_trace("Calculating needed probes"); /* This code probably needs optimization * ptest -x with 100 nodes, 100 clones and clone-max=100: With probes: ptest[14781]: 2010/09/27_17:56:46 notice: TRACE: do_calculations: pengine.c:258 Calculate cluster status ptest[14781]: 2010/09/27_17:56:46 notice: TRACE: do_calculations: pengine.c:278 Applying placement constraints ptest[14781]: 2010/09/27_17:56:47 notice: TRACE: do_calculations: pengine.c:285 Create internal constraints ptest[14781]: 2010/09/27_17:56:47 notice: TRACE: do_calculations: pengine.c:292 Check actions ptest[14781]: 2010/09/27_17:56:48 notice: TRACE: do_calculations: pengine.c:299 Allocate resources ptest[14781]: 2010/09/27_17:56:48 notice: TRACE: stage5: allocate.c:881 Allocating services ptest[14781]: 2010/09/27_17:56:49 notice: TRACE: stage5: allocate.c:894 Calculating needed probes ptest[14781]: 2010/09/27_17:56:51 notice: TRACE: stage5: allocate.c:899 Creating actions ptest[14781]: 2010/09/27_17:56:52 notice: TRACE: stage5: allocate.c:905 Creating done ptest[14781]: 2010/09/27_17:56:52 notice: TRACE: do_calculations: pengine.c:306 Processing fencing and shutdown cases ptest[14781]: 2010/09/27_17:56:52 notice: TRACE: do_calculations: pengine.c:313 Applying ordering constraints 36s ptest[14781]: 2010/09/27_17:57:28 notice: TRACE: do_calculations: pengine.c:320 Create transition graph Without probes: ptest[14637]: 2010/09/27_17:56:21 notice: TRACE: do_calculations: pengine.c:258 Calculate cluster status ptest[14637]: 2010/09/27_17:56:22 notice: TRACE: do_calculations: pengine.c:278 Applying placement constraints ptest[14637]: 2010/09/27_17:56:22 notice: TRACE: do_calculations: pengine.c:285 Create internal constraints ptest[14637]: 2010/09/27_17:56:22 notice: TRACE: do_calculations: pengine.c:292 Check actions ptest[14637]: 2010/09/27_17:56:23 notice: TRACE: do_calculations: pengine.c:299 Allocate resources ptest[14637]: 2010/09/27_17:56:23 notice: TRACE: stage5: allocate.c:881 Allocating services ptest[14637]: 2010/09/27_17:56:24 notice: TRACE: stage5: allocate.c:899 Creating actions ptest[14637]: 2010/09/27_17:56:25 notice: TRACE: stage5: allocate.c:905 Creating done ptest[14637]: 2010/09/27_17:56:25 notice: TRACE: do_calculations: pengine.c:306 Processing fencing and shutdown cases ptest[14637]: 2010/09/27_17:56:25 notice: TRACE: do_calculations: pengine.c:313 Applying ordering constraints ptest[14637]: 2010/09/27_17:56:25 notice: TRACE: do_calculations: pengine.c:320 Create transition graph */ probe_resources(data_set); } crm_trace("Creating actions"); gIter = data_set->resources; for(; gIter != NULL; gIter = gIter->next) { resource_t *rsc = (resource_t*)gIter->data; rsc->cmds->create_actions(rsc, data_set); } crm_trace("Creating done"); return TRUE; } static gboolean is_managed(const resource_t *rsc) { GListPtr gIter = rsc->children; if(is_set(rsc->flags, pe_rsc_managed)) { return TRUE; } for(; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t*)gIter->data; if(is_managed(child_rsc)) { return TRUE; } } return FALSE; } static gboolean any_managed_resouces(pe_working_set_t *data_set) { GListPtr gIter = data_set->resources; for(; gIter != NULL; gIter = gIter->next) { resource_t *rsc = (resource_t*)gIter->data; 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 *dc_fence = NULL; action_t *stonith_op = NULL; action_t *last_stonith = NULL; gboolean integrity_lost = FALSE; action_t *ready = get_pseudo_op(STONITH_UP, data_set); action_t *all_stopped = get_pseudo_op(ALL_STOPPED, data_set); action_t *done = get_pseudo_op(STONITH_DONE, data_set); gboolean need_stonith = FALSE; GListPtr gIter = data_set->nodes; crm_debug_3("Processing fencing and shutdown cases"); if(is_set(data_set->flags, pe_flag_stonith_enabled) && (is_set(data_set->flags, pe_flag_have_quorum) || data_set->no_quorum_policy == no_quorum_ignore || data_set->no_quorum_policy == no_quorum_suicide)) { need_stonith = TRUE; } if(need_stonith && any_managed_resouces(data_set) == FALSE) { crm_notice("Delaying fencing operations until there are resources to manage"); need_stonith = FALSE; } for(; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t*)gIter->data; 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_runnable_left); order_actions(stonith_op, all_stopped, pe_order_implies_then); clear_bit_inplace(ready->flags, pe_action_optional); if(node->details->is_dc) { dc_down = stonith_op; dc_fence = stonith_op; } else { if(last_stonith) { order_actions(last_stonith, stonith_op, pe_order_optional); } last_stonith = stonith_op; } } else if(node->details->online && node->details->shutdown) { action_t *down_op = NULL; crm_notice("Scheduling Node %s for shutdown", node->details->uname); down_op = custom_action( NULL, crm_strdup(CRM_OP_SHUTDOWN), CRM_OP_SHUTDOWN, node, FALSE, TRUE, data_set); shutdown_constraints(node, down_op, data_set); add_hash_param(down_op->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE); if(node->details->is_dc) { dc_down = down_op; } } if(node->details->unclean && stonith_op == NULL) { integrity_lost = TRUE; pe_warn("Node %s is unclean!", node->details->uname); } } if(integrity_lost) { if(is_set(data_set->flags, pe_flag_stonith_enabled) == FALSE) { pe_warn("YOUR RESOURCES ARE NOW LIKELY COMPROMISED"); pe_err("ENABLE STONITH TO KEEP YOUR RESOURCES SAFE"); } else if(is_set(data_set->flags, pe_flag_have_quorum) == FALSE) { crm_notice("Cannot fence unclean nodes until quorum is" " attained (or no-quorum-policy is set to ignore)"); } } if(dc_down != NULL) { GListPtr shutdown_matches = find_actions( data_set->actions, CRM_OP_SHUTDOWN, NULL); crm_debug_2("Ordering shutdowns before %s on %s (DC)", dc_down->task, dc_down->node->details->uname); add_hash_param(dc_down->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE); gIter = shutdown_matches; for(; gIter != NULL; gIter = gIter->next) { action_t *node_stop = (action_t*)gIter->data; 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_optional); } if(last_stonith && dc_down != last_stonith) { order_actions(last_stonith, dc_down, pe_order_optional); } g_list_free(shutdown_matches); } if(last_stonith) { order_actions(last_stonith, done, pe_order_implies_then); } else if(dc_fence) { order_actions(dc_down, done, pe_order_implies_then); } order_actions(ready, done, pe_order_optional); return TRUE; } /* * Determin the sets of independant actions and the correct order for the * actions in each set. * * Mark dependencies of un-runnable actions un-runnable * */ static GListPtr find_actions_by_task(GListPtr actions, resource_t *rsc, const char *original_key) { GListPtr list = NULL; list = find_actions(actions, original_key, NULL); if(list == NULL) { /* we're potentially searching a child of the original resource */ char *key = NULL; char *tmp = NULL; char *task = NULL; int interval = 0; if(parse_op_key(original_key, &tmp, &task, &interval)) { key = generate_op_key(rsc->id, task, interval); /* crm_err("looking up %s instead of %s", key, original_key); */ /* slist_iter(action, action_t, actions, lpc, */ /* crm_err(" - %s", action->uuid)); */ list = find_actions(actions, key, NULL); } else { crm_err("search key: %s", original_key); } crm_free(key); crm_free(tmp); crm_free(task); } return list; } static void rsc_order_then( action_t *lh_action, resource_t *rsc, order_constraint_t *order) { GListPtr gIter = NULL; GListPtr rh_actions = NULL; action_t *rh_action = NULL; enum pe_ordering type = order->type; 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_prepend(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; } if(lh_action->rsc == rsc && is_set(lh_action->flags, pe_action_dangle)) { crm_trace("Detected dangling operation %s -> %s", lh_action->uuid, order->rh_action_task); clear_bit_inplace(type, pe_order_implies_then); } gIter = rh_actions; for(; gIter != NULL; gIter = gIter->next) { action_t *rh_action_iter = (action_t*)gIter->data; if(lh_action) { order_actions(lh_action, rh_action_iter, type); } else if(type & pe_order_implies_then) { update_action_flags(rh_action_iter, pe_action_runnable|pe_action_clear); crm_warn("Unrunnable %s 0x%.6x", rh_action_iter->uuid, type); } else { crm_warn("neither %s 0x%.6x", rh_action_iter->uuid, type); } } - pe_free_shallow_adv(rh_actions, FALSE); + g_list_free(rh_actions); } static void rsc_order_first(resource_t *lh_rsc, order_constraint_t *order, pe_working_set_t *data_set) { GListPtr gIter = NULL; GListPtr lh_actions = NULL; action_t *lh_action = order->lh_action; resource_t *rh_rsc = order->rh_rsc; crm_debug_3("Processing LH of ordering constraint %d", order->id); CRM_ASSERT(lh_rsc != NULL); if(lh_action != NULL) { lh_actions = g_list_prepend(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; parse_op_key(order->lh_action_task, &rsc_id, &op_type, &interval); key = generate_op_key(lh_rsc->id, op_type, interval); if(lh_rsc->fns->state(lh_rsc, TRUE) != RSC_ROLE_STOPPED || safe_str_neq(op_type, RSC_STOP)) { crm_debug_4("No LH-Side (%s/%s) found for constraint %d with %s - creating", lh_rsc->id, order->lh_action_task, order->id, order->rh_action_task); lh_action = custom_action(lh_rsc, key, op_type, NULL, TRUE, TRUE, data_set); lh_actions = g_list_prepend(NULL, lh_action); } else { crm_free(key); crm_debug_4("No LH-Side (%s/%s) found for constraint %d with %s - ignoring", lh_rsc->id, order->lh_action_task, order->id, order->rh_action_task); } crm_free(op_type); crm_free(rsc_id); } gIter = lh_actions; for(; gIter != NULL; gIter = gIter->next) { action_t *lh_action_iter = (action_t*)gIter->data; if(rh_rsc == NULL && order->rh_action) { rh_rsc = order->rh_action->rsc; } if(rh_rsc) { rsc_order_then(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); + g_list_free(lh_actions); } extern gboolean update_action(action_t *action); gboolean stage7(pe_working_set_t *data_set) { GListPtr gIter = NULL; crm_debug_4("Applying ordering constraints"); /* Don't ask me why, but apparently they need to be processed in * the order they were created in... go figure * * Also g_list_prepend() has horrendous performance characteristics * So we need to use g_list_prepend() and then reverse the list here */ data_set->ordering_constraints = g_list_reverse( data_set->ordering_constraints); gIter = data_set->ordering_constraints; for(; gIter != NULL; gIter = gIter->next) { order_constraint_t *order = (order_constraint_t*)gIter->data; resource_t *rsc = order->lh_rsc; crm_debug_3("Applying ordering constraint: %d", order->id); if(rsc != NULL) { crm_debug_4("rsc_action-to-*"); rsc_order_first(rsc, order, data_set); continue; } rsc = order->rh_rsc; if(rsc != NULL) { crm_debug_4("action-to-rsc_action"); rsc_order_then(order->lh_action, rsc, order); } else { crm_debug_4("action-to-action"); order_actions( order->lh_action, order->rh_action, order->type); } } crm_debug_2("Updating %d actions", g_list_length(data_set->actions)); gIter = data_set->actions; for(; gIter != NULL; gIter = gIter->next) { action_t *action = (action_t*)gIter->data; update_action(action); } crm_debug_2("Processing migrations"); gIter = data_set->resources; for(; gIter != NULL; gIter = gIter->next) { resource_t *rsc = (resource_t*)gIter->data; rsc_migrate_reload(rsc, data_set); LogActions(rsc, data_set); } return TRUE; } static gint sort_notify_entries(gconstpointer a, gconstpointer b) { int tmp; const notify_entry_t *entry_a = a; const notify_entry_t *entry_b = b; if(entry_a == NULL && entry_b == NULL) { return 0; } if(entry_a == NULL) { return 1; } if(entry_b == NULL) { return -1; } if(entry_a->rsc == NULL && entry_b->rsc == NULL) { return 0; } if(entry_a->rsc == NULL) { return 1; } if(entry_b->rsc == NULL) { return -1; } tmp = strcmp(entry_a->rsc->id, entry_b->rsc->id); if(tmp != 0) { return tmp; } if(entry_a->node == NULL && entry_b->node == NULL) { return 0; } if(entry_a->node == NULL) { return 1; } if(entry_b->node == NULL) { return -1; } return strcmp(entry_a->node->details->id, entry_b->node->details->id); } static void expand_list(GListPtr list, char **rsc_list, char **node_list) { GListPtr gIter = list; const char *uname = NULL; const char *rsc_id = NULL; const char *last_rsc_id = NULL; if(list == NULL) { *rsc_list = crm_strdup(" "); if(node_list) { *node_list = crm_strdup(" "); } return; } *rsc_list = NULL; if(node_list) { *node_list = NULL; } for(; gIter != NULL; gIter = gIter->next) { notify_entry_t *entry = (notify_entry_t*)gIter->data; CRM_CHECK(entry != NULL, continue); CRM_CHECK(entry->rsc != NULL, continue); CRM_CHECK(node_list == NULL || entry->node != NULL, continue); uname = NULL; rsc_id = entry->rsc->id; CRM_ASSERT(rsc_id != NULL); /* filter dups */ if(safe_str_eq(rsc_id, last_rsc_id)) { continue; } last_rsc_id = rsc_id; if(rsc_list != NULL) { int existing_len = 0; int len = 2 + strlen(rsc_id); /* +1 space, +1 EOS */ if(rsc_list && *rsc_list) { existing_len = strlen(*rsc_list); } crm_debug_5("Adding %s (%dc) at offset %d", rsc_id, len-2, existing_len); crm_realloc(*rsc_list, len + existing_len); sprintf(*rsc_list + existing_len, "%s ", rsc_id); } if(entry->node != NULL) { uname = entry->node->details->uname; } if(node_list != NULL && uname) { int existing_len = 0; int len = 2 + strlen(uname); if(node_list && *node_list) { existing_len = strlen(*node_list); } crm_debug_5("Adding %s (%dc) at offset %d", uname, len-2, existing_len); crm_realloc(*node_list, len + existing_len); sprintf(*node_list + existing_len, "%s ", uname); } } } static void dup_attr(gpointer key, gpointer value, gpointer user_data) { add_hash_param(user_data, key, 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(is_set(op->flags, pe_action_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, is_set(op->flags, pe_action_optional), TRUE, data_set); g_hash_table_foreach(op->meta, dup_attr, trigger->meta); g_hash_table_foreach(n_data->keys, dup_attr, trigger->meta); /* pseudo_notify before notify */ crm_debug_3("Ordering %s before %s (%d->%d)", op->uuid, trigger->uuid, trigger->id, op->id); order_actions(op, trigger, pe_order_optional); order_actions(trigger, confirm, pe_order_optional); return trigger; } static void pe_post_notify(resource_t *rsc, node_t *node, notify_data_t *n_data, pe_working_set_t *data_set) { action_t *notify = NULL; CRM_CHECK(rsc != NULL, return); if(n_data->post == NULL) { return; /* Nothing to do */ } notify = pe_notify(rsc, node, n_data->post, n_data->post_done, n_data, data_set); if(notify != NULL) { notify->priority = INFINITY; } if(n_data->post_done) { GListPtr gIter = rsc->actions; for(; gIter != NULL; gIter = gIter->next) { action_t *mon = (action_t*)gIter->data; const char *interval = g_hash_table_lookup(mon->meta, "interval"); if(interval == NULL || safe_str_eq(interval, "0")) { crm_debug_3("Skipping %s: interval", mon->uuid); continue; } else if(safe_str_eq(mon->task, "cancel")) { crm_debug_3("Skipping %s: cancel", mon->uuid); continue; } order_actions(n_data->post_done, mon, pe_order_optional); } } } notify_data_t * create_notification_boundaries( resource_t *rsc, const char *action, action_t *start, action_t *end, pe_working_set_t *data_set) { /* Create the pseudo ops that preceed and follow the actual notifications */ /* * Creates two sequences (conditional on start and end being supplied): * pre_notify -> pre_notify_complete -> start, and * end -> post_notify -> post_notify_complete * * 'start' and 'end' may be the same event or ${X} and ${X}ed as per clones */ char *key = NULL; notify_data_t *n_data = NULL; if(is_not_set(rsc->flags, pe_rsc_notify)) { return NULL; } crm_malloc0(n_data, sizeof(notify_data_t)); n_data->action = action; n_data->keys = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); if(start) { /* create pre-event notification wrappers */ key = generate_notify_key(rsc->id, "pre", start->task); n_data->pre = custom_action( rsc, key, RSC_NOTIFY, NULL, is_set(start->flags, pe_action_optional), TRUE, data_set); update_action_flags(n_data->pre, pe_action_pseudo); update_action_flags(n_data->pre, pe_action_runnable); add_hash_param(n_data->pre->meta, "notify_type", "pre"); add_hash_param(n_data->pre->meta, "notify_operation", n_data->action); /* create pre_notify_complete */ key = generate_notify_key(rsc->id, "confirmed-pre", start->task); n_data->pre_done = custom_action( rsc, key, RSC_NOTIFIED, NULL, is_set(start->flags, pe_action_optional), TRUE, data_set); update_action_flags(n_data->pre_done, pe_action_pseudo); update_action_flags(n_data->pre_done, pe_action_runnable); add_hash_param(n_data->pre_done->meta, "notify_type", "pre"); add_hash_param(n_data->pre_done->meta, "notify_operation", n_data->action); order_actions(n_data->pre_done, start, pe_order_optional); order_actions(n_data->pre, n_data->pre_done, pe_order_optional); } if(end) { /* create post-event notification wrappers */ key = generate_notify_key(rsc->id, "post", end->task); n_data->post = custom_action( rsc, key, RSC_NOTIFY, NULL, is_set(end->flags, pe_action_optional), TRUE, data_set); n_data->post->priority = INFINITY; update_action_flags(n_data->post, pe_action_pseudo); if(is_set(end->flags, pe_action_runnable)) { update_action_flags(n_data->post, pe_action_runnable); } else { update_action_flags(n_data->post, pe_action_runnable|pe_action_clear); } add_hash_param(n_data->post->meta, "notify_type", "post"); add_hash_param(n_data->post->meta, "notify_operation", n_data->action); /* create post_notify_complete */ key = generate_notify_key(rsc->id, "confirmed-post", end->task); n_data->post_done = custom_action( rsc, key, RSC_NOTIFIED, NULL, is_set(end->flags, pe_action_optional), TRUE, data_set); n_data->post_done->priority = INFINITY; update_action_flags(n_data->post_done, pe_action_pseudo); if(is_set(end->flags, pe_action_runnable)) { update_action_flags(n_data->post_done, pe_action_runnable); } else { update_action_flags(n_data->post_done, pe_action_runnable|pe_action_clear); } add_hash_param(n_data->post_done->meta, "notify_type", "pre"); add_hash_param(n_data->post_done->meta, "notify_operation", n_data->action); order_actions(end, n_data->post, pe_order_implies_then); order_actions(n_data->post, n_data->post_done, pe_order_implies_then); } if(start && end) { order_actions(n_data->pre_done, n_data->post, pe_order_optional); } if(safe_str_eq(action, RSC_STOP)) { action_t *all_stopped = get_pseudo_op(ALL_STOPPED, data_set); order_actions(n_data->post_done, all_stopped, pe_order_optional); } return n_data; } void collect_notification_data(resource_t *rsc, gboolean state, gboolean activity, notify_data_t *n_data) { if(rsc->children) { GListPtr gIter = rsc->children; for(; gIter != NULL; gIter = gIter->next) { resource_t *child = (resource_t*)gIter->data; collect_notification_data(child, state, activity, n_data); } return; } if(state) { notify_entry_t *entry = NULL; crm_malloc0(entry, sizeof(notify_entry_t)); entry->rsc = rsc; if(rsc->running_on) { /* we only take the first one */ entry->node = rsc->running_on->data; } crm_debug_2("%s state: %s", rsc->id, role2text(rsc->role)); switch(rsc->role) { case RSC_ROLE_STOPPED: n_data->inactive = g_list_prepend(n_data->inactive, entry); break; case RSC_ROLE_STARTED: n_data->active = g_list_prepend(n_data->active, entry); break; case RSC_ROLE_SLAVE: n_data->slave = g_list_prepend(n_data->slave, entry); break; case RSC_ROLE_MASTER: n_data->master = g_list_prepend(n_data->master, entry); break; default: crm_err("Unsupported notify role"); crm_free(entry); break; } } if(activity) { notify_entry_t *entry = NULL; enum action_tasks task; GListPtr gIter = rsc->actions; for(; gIter != NULL; gIter = gIter->next) { action_t *op = (action_t*)gIter->data; if(is_set(op->flags, pe_action_optional) == FALSE && op->node != NULL) { crm_malloc0(entry, sizeof(notify_entry_t)); entry->node = op->node; entry->rsc = rsc; task = text2task(op->task); switch(task) { case start_rsc: n_data->start = g_list_prepend(n_data->start, entry); break; case stop_rsc: n_data->stop = g_list_prepend(n_data->stop, entry); break; case action_promote: n_data->promote = g_list_prepend(n_data->promote, entry); break; case action_demote: n_data->demote = g_list_prepend(n_data->demote, entry); break; default: crm_free(entry); break; } } } } } gboolean expand_notification_data(notify_data_t *n_data) { /* Expand the notification entries into a key=value hashtable * This hashtable is later used in action2xml() */ gboolean required = FALSE; char *rsc_list = NULL; char *node_list = NULL; if(n_data->stop) { n_data->stop = g_list_sort(n_data->stop, sort_notify_entries); } expand_list(n_data->stop, &rsc_list, &node_list); if(rsc_list != NULL && safe_str_neq(" ", rsc_list)) { if(safe_str_eq(n_data->action, RSC_STOP)) { required = TRUE; } } g_hash_table_insert(n_data->keys, crm_strdup("notify_stop_resource"), rsc_list); g_hash_table_insert(n_data->keys, crm_strdup("notify_stop_uname"), node_list); if(n_data->start) { n_data->start = g_list_sort(n_data->start, sort_notify_entries); if(rsc_list && safe_str_eq(n_data->action, RSC_START)) { required = TRUE; } } expand_list(n_data->start, &rsc_list, &node_list); g_hash_table_insert(n_data->keys, crm_strdup("notify_start_resource"), rsc_list); g_hash_table_insert(n_data->keys, crm_strdup("notify_start_uname"), node_list); if(n_data->demote) { n_data->demote = g_list_sort(n_data->demote, sort_notify_entries); if(safe_str_eq(n_data->action, RSC_DEMOTE)) { required = TRUE; } } expand_list(n_data->demote, &rsc_list, &node_list); g_hash_table_insert(n_data->keys, crm_strdup("notify_demote_resource"), rsc_list); g_hash_table_insert(n_data->keys, crm_strdup("notify_demote_uname"), node_list); if(n_data->promote) { n_data->promote = g_list_sort(n_data->promote, sort_notify_entries); if(safe_str_eq(n_data->action, RSC_PROMOTE)) { required = TRUE; } } expand_list(n_data->promote, &rsc_list, &node_list); g_hash_table_insert(n_data->keys, crm_strdup("notify_promote_resource"), rsc_list); g_hash_table_insert(n_data->keys, crm_strdup("notify_promote_uname"), node_list); if(n_data->active) { n_data->active = g_list_sort(n_data->active, sort_notify_entries); } expand_list(n_data->active, &rsc_list, &node_list); g_hash_table_insert(n_data->keys, crm_strdup("notify_active_resource"), rsc_list); g_hash_table_insert(n_data->keys, crm_strdup("notify_active_uname"), node_list); if(n_data->slave) { n_data->slave = g_list_sort(n_data->slave, sort_notify_entries); } expand_list(n_data->slave, &rsc_list, &node_list); g_hash_table_insert(n_data->keys, crm_strdup("notify_slave_resource"), rsc_list); g_hash_table_insert(n_data->keys, crm_strdup("notify_slave_uname"), node_list); if(n_data->master) { n_data->master = g_list_sort(n_data->master, sort_notify_entries); } expand_list(n_data->master, &rsc_list, &node_list); g_hash_table_insert(n_data->keys, crm_strdup("notify_master_resource"), rsc_list); g_hash_table_insert(n_data->keys, crm_strdup("notify_master_uname"), node_list); if(n_data->inactive) { n_data->inactive = g_list_sort(n_data->inactive, sort_notify_entries); } expand_list(n_data->inactive, &rsc_list, NULL); g_hash_table_insert(n_data->keys, crm_strdup("notify_inactive_resource"), rsc_list); if(required && n_data->pre) { update_action_flags(n_data->pre, pe_action_optional|pe_action_clear); update_action_flags(n_data->pre_done, pe_action_optional|pe_action_clear); } if(required && n_data->post) { update_action_flags(n_data->post, pe_action_optional|pe_action_clear); update_action_flags(n_data->post_done, pe_action_optional|pe_action_clear); } return required; } void create_notifications(resource_t *rsc, notify_data_t *n_data, pe_working_set_t *data_set) { GListPtr gIter = NULL; action_t *stop = NULL; action_t *start = NULL; enum action_tasks task = text2task(n_data->action); if(rsc->children) { gIter = rsc->children; for(; gIter != NULL; gIter = gIter->next) { resource_t *child = (resource_t*)gIter->data; create_notifications(child, n_data, data_set); } return; } /* Copy notification details into standard ops */ gIter = rsc->actions; for(; gIter != NULL; gIter = gIter->next) { action_t *op = (action_t*)gIter->data; if(is_set(op->flags, pe_action_optional) == FALSE && op->node != NULL) { enum action_tasks t = text2task(op->task); switch(t) { case start_rsc: case stop_rsc: case action_promote: case action_demote: g_hash_table_foreach(n_data->keys, dup_attr, op->meta); break; default: break; } } } crm_debug_2("Creating notificaitons for: %s.%s (%s->%s)", n_data->action, rsc->id, role2text(rsc->role), role2text(rsc->next_role)); stop = find_first_action(rsc->actions, NULL, RSC_STOP, NULL); start = find_first_action(rsc->actions, NULL, RSC_START, NULL); /* stop / demote */ if(rsc->role != RSC_ROLE_STOPPED) { if(task == stop_rsc || task == action_demote) { gIter = rsc->running_on; for(; gIter != NULL; gIter = gIter->next) { node_t *current_node = (node_t*)gIter->data; pe_notify(rsc, current_node, n_data->pre, n_data->pre_done, n_data, data_set); if(task == action_demote || stop == NULL || is_set(stop->flags, pe_action_optional)) { pe_post_notify(rsc, current_node, n_data, data_set); } } } } /* start / promote */ if(rsc->next_role != RSC_ROLE_STOPPED) { if(rsc->allocated_to == NULL) { pe_proc_err("Next role '%s' but %s is not allocated", role2text(rsc->next_role), rsc->id); } else if(task == start_rsc || task == action_promote) { if(task != start_rsc || start == NULL || is_set(start->flags, pe_action_optional)) { pe_notify(rsc, rsc->allocated_to, n_data->pre, n_data->pre_done, n_data, data_set); } pe_post_notify(rsc, rsc->allocated_to, n_data, data_set); } } } void free_notification_data(notify_data_t *n_data) { if(n_data == NULL) { return; } - pe_free_shallow(n_data->stop); - pe_free_shallow(n_data->start); - pe_free_shallow(n_data->demote); - pe_free_shallow(n_data->promote); - pe_free_shallow(n_data->master); - pe_free_shallow(n_data->slave); - pe_free_shallow(n_data->active); - pe_free_shallow(n_data->inactive); + slist_basic_destroy(n_data->stop); + slist_basic_destroy(n_data->start); + slist_basic_destroy(n_data->demote); + slist_basic_destroy(n_data->promote); + slist_basic_destroy(n_data->master); + slist_basic_destroy(n_data->slave); + slist_basic_destroy(n_data->active); + slist_basic_destroy(n_data->inactive); g_hash_table_destroy(n_data->keys); crm_free(n_data); } int transition_id = -1; /* * Create a dependency graph to send to the transitioner (via the CRMd) */ gboolean stage8(pe_working_set_t *data_set) { GListPtr gIter = NULL; const char *value = NULL; transition_id++; crm_debug_2("Creating transition graph %d.", transition_id); data_set->graph = create_xml_node(NULL, XML_TAG_GRAPH); value = pe_pref(data_set->config_hash, "cluster-delay"); crm_xml_add(data_set->graph, "cluster-delay", value); value = pe_pref(data_set->config_hash, "stonith-timeout"); crm_xml_add(data_set->graph, "stonith-timeout", value); crm_xml_add(data_set->graph, "failed-stop-offset", "INFINITY"); if(is_set(data_set->flags, pe_flag_start_failure_fatal)) { crm_xml_add(data_set->graph, "failed-start-offset", "INFINITY"); } else { crm_xml_add(data_set->graph, "failed-start-offset", "1"); } value = pe_pref(data_set->config_hash, "batch-limit"); crm_xml_add(data_set->graph, "batch-limit", value); crm_xml_add_int(data_set->graph, "transition_id", transition_id); /* errors... slist_iter(action, action_t, action_list, lpc, if(action->optional == FALSE && action->runnable == FALSE) { print_action("Ignoring", action, TRUE); } ); */ gIter = data_set->resources; for(; gIter != NULL; gIter = gIter->next) { resource_t *rsc = (resource_t*)gIter->data; 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"); gIter = data_set->actions; for(; gIter != NULL; gIter = gIter->next) { action_t *action = (action_t*)gIter->data; 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 %d order cons: %p", g_list_length(data_set->ordering_constraints), data_set->ordering_constraints); pe_free_ordering(data_set->ordering_constraints); data_set->ordering_constraints = NULL; crm_debug_3("deleting %d node cons: %p", g_list_length(data_set->placement_constraints), data_set->placement_constraints); pe_free_rsc_to_node(data_set->placement_constraints); data_set->placement_constraints = NULL; crm_debug_3("deleting %d inter-resource cons: %p", g_list_length(data_set->colocation_constraints), data_set->colocation_constraints); - pe_free_shallow(data_set->colocation_constraints); + slist_basic_destroy(data_set->colocation_constraints); data_set->colocation_constraints = NULL; cleanup_calculations(data_set); } diff --git a/pengine/constraints.c b/pengine/constraints.c index a261abb4fb..e8fe36d507 100644 --- a/pengine/constraints.c +++ b/pengine/constraints.c @@ -1,1344 +1,1363 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include enum pe_order_kind { pe_order_kind_optional, pe_order_kind_mandatory, pe_order_kind_serialize, }; enum pe_ordering get_flags( const char *id, enum pe_order_kind kind, const char *action_first, const char *action_then, gboolean invert); gboolean unpack_constraints(xmlNode * xml_constraints, pe_working_set_t *data_set) { - xmlNode *lifetime = NULL; - xml_child_iter( - xml_constraints, xml_obj, - - const char *id = crm_element_value(xml_obj, XML_ATTR_ID); - if(id == NULL) { - crm_config_err("Constraint <%s...> must have an id", - crm_element_name(xml_obj)); - continue; - } + xmlNode *xml_obj = NULL; + xmlNode *lifetime = NULL; + for(xml_obj = xml_constraints; xml_obj != NULL; xml_obj = xml_obj->next) { + 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); + crm_debug_3("Processing constraint %s %s", + crm_element_name(xml_obj),id); - lifetime = first_named_child(xml_obj, "lifetime"); - if(lifetime) { - crm_config_warn("Support for the lifetime tag, used by %s, is deprecated." - " The rules it contains should instead be direct decendants of the constraint object", id); - } + lifetime = first_named_child(xml_obj, "lifetime"); + if(lifetime) { + crm_config_warn("Support for the lifetime tag, used by %s, is deprecated." + " The rules it contains should instead be direct decendants of the constraint object", id); + } - if(test_ruleset(lifetime, NULL, data_set->now) == FALSE) { - crm_info("Constraint %s %s is not active", - crm_element_name(xml_obj), id); + 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_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_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 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)); - } - ); + } else { + pe_err("Unsupported constraint type: %s", + crm_element_name(xml_obj)); + } + } - return TRUE; + return TRUE; } static const char * invert_action(const char *action) { - if(safe_str_eq(action, RSC_START)) { - return RSC_STOP; + if(safe_str_eq(action, RSC_START)) { + return RSC_STOP; - } else if(safe_str_eq(action, RSC_STOP)) { - return RSC_START; + } else if(safe_str_eq(action, RSC_STOP)) { + return RSC_START; - } else if(safe_str_eq(action, RSC_PROMOTE)) { - return RSC_DEMOTE; + } else if(safe_str_eq(action, RSC_PROMOTE)) { + return RSC_DEMOTE; - } else if(safe_str_eq(action, RSC_DEMOTE)) { - return RSC_PROMOTE; + } else if(safe_str_eq(action, RSC_DEMOTE)) { + return RSC_PROMOTE; - } else if(safe_str_eq(action, RSC_PROMOTED)) { - return RSC_DEMOTED; + } else if(safe_str_eq(action, RSC_PROMOTED)) { + return RSC_DEMOTED; - } else if(safe_str_eq(action, RSC_DEMOTED)) { - return RSC_PROMOTED; + } else if(safe_str_eq(action, RSC_DEMOTED)) { + return RSC_PROMOTED; - } else if(safe_str_eq(action, RSC_STARTED)) { - return RSC_STOPPED; + } else if(safe_str_eq(action, RSC_STARTED)) { + return RSC_STOPPED; - } else if(safe_str_eq(action, RSC_STOPPED)) { - return RSC_STARTED; - } - crm_config_warn("Unknown action: %s", action); - return NULL; + } else if(safe_str_eq(action, RSC_STOPPED)) { + return RSC_STARTED; + } + crm_config_warn("Unknown action: %s", action); + return NULL; } static enum pe_order_kind get_ordering_type(xmlNode *xml_obj) { enum pe_order_kind kind_e = pe_order_kind_mandatory; const char *kind = crm_element_value(xml_obj, XML_ORDER_ATTR_KIND); if(kind == NULL) { const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE); kind_e = pe_order_kind_mandatory; if(score) { int score_i = char2score(score); if(score_i == 0) { kind_e = pe_order_kind_optional; } - /* } else if(rsc_then->variant == pe_native && rsc_first->variant > pe_group) { */ - /* kind_e = pe_order_kind_optional; */ + /* } else if(rsc_then->variant == pe_native && rsc_first->variant > pe_group) { */ + /* kind_e = pe_order_kind_optional; */ } } else if(safe_str_eq(kind, "Mandatory")) { kind_e = pe_order_kind_mandatory; } else if(safe_str_eq(kind, "Optional")) { kind_e = pe_order_kind_optional; } else if(safe_str_eq(kind, "Serialize")) { kind_e = pe_order_kind_serialize; } else { const char *id = crm_element_value(xml_obj, XML_ATTR_ID); crm_config_err("Constraint %s: Unknown type '%s'", id, kind); } return kind_e; } static gboolean unpack_simple_rsc_order(xmlNode * xml_obj, pe_working_set_t *data_set) { - int order_id = 0; - resource_t *rsc_then = NULL; - resource_t *rsc_first = NULL; - gboolean invert_bool = TRUE; - enum pe_order_kind kind = pe_order_kind_mandatory; - enum pe_ordering cons_weight = pe_order_optional; - - const char *id_first = NULL; - const char *id_then = NULL; - const char *action_then = NULL; - const char *action_first = NULL; - const char *instance_then = NULL; - const char *instance_first = NULL; + int order_id = 0; + resource_t *rsc_then = NULL; + resource_t *rsc_first = NULL; + gboolean invert_bool = TRUE; + enum pe_order_kind kind = pe_order_kind_mandatory; + enum pe_ordering cons_weight = pe_order_optional; + + const char *id_first = NULL; + const char *id_then = NULL; + const char *action_then = NULL; + const char *action_first = NULL; + const char *instance_then = NULL; + const char *instance_first = NULL; - const char *id = crm_element_value(xml_obj, XML_ATTR_ID); - const char *invert = crm_element_value(xml_obj, XML_CONS_ATTR_SYMMETRICAL); + const char *id = crm_element_value(xml_obj, XML_ATTR_ID); + const char *invert = crm_element_value(xml_obj, XML_CONS_ATTR_SYMMETRICAL); - crm_str_to_boolean(invert, &invert_bool); + crm_str_to_boolean(invert, &invert_bool); - if(xml_obj == NULL) { - crm_config_err("No constraint object to process."); - return FALSE; + 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; - } + } else if(id == NULL) { + crm_config_err("%s constraint must have an id", + crm_element_name(xml_obj)); + return FALSE; + } - id_then = crm_element_value(xml_obj, XML_ORDER_ATTR_THEN); - id_first = crm_element_value(xml_obj, XML_ORDER_ATTR_FIRST); + id_then = crm_element_value(xml_obj, XML_ORDER_ATTR_THEN); + id_first = crm_element_value(xml_obj, XML_ORDER_ATTR_FIRST); - action_then = crm_element_value(xml_obj, XML_ORDER_ATTR_THEN_ACTION); - action_first = crm_element_value(xml_obj, XML_ORDER_ATTR_FIRST_ACTION); + action_then = crm_element_value(xml_obj, XML_ORDER_ATTR_THEN_ACTION); + action_first = crm_element_value(xml_obj, XML_ORDER_ATTR_FIRST_ACTION); - instance_then = crm_element_value(xml_obj, XML_ORDER_ATTR_THEN_INSTANCE); - instance_first = crm_element_value(xml_obj, XML_ORDER_ATTR_FIRST_INSTANCE); + instance_then = crm_element_value(xml_obj, XML_ORDER_ATTR_THEN_INSTANCE); + instance_first = crm_element_value(xml_obj, XML_ORDER_ATTR_FIRST_INSTANCE); - if(action_first == NULL) { - action_first = RSC_START; - } - if(action_then == NULL) { - action_then = action_first; - } + if(action_first == NULL) { + action_first = RSC_START; + } + if(action_then == NULL) { + action_then = action_first; + } - if(id_then == NULL || id_first == NULL) { - crm_config_err("Constraint %s needs two sides lh: %s rh: %s", - id, crm_str(id_then), crm_str(id_first)); - return FALSE; - } + if(id_then == NULL || id_first == NULL) { + crm_config_err("Constraint %s needs two sides lh: %s rh: %s", + id, crm_str(id_then), crm_str(id_first)); + return FALSE; + } - rsc_then = pe_find_resource(data_set->resources, id_then); - rsc_first = pe_find_resource(data_set->resources, id_first); + rsc_then = pe_find_resource(data_set->resources, id_then); + rsc_first = pe_find_resource(data_set->resources, id_first); - if(rsc_then == NULL) { - crm_config_err("Constraint %s: no resource found for name '%s'", id, id_then); - return FALSE; + if(rsc_then == NULL) { + crm_config_err("Constraint %s: no resource found for name '%s'", id, id_then); + return FALSE; - } else if(rsc_first == NULL) { - crm_config_err("Constraint %s: no resource found for name '%s'", id, id_first); - return FALSE; + } else if(rsc_first == NULL) { + crm_config_err("Constraint %s: no resource found for name '%s'", id, id_first); + return FALSE; - } else if(instance_then && rsc_then->variant < pe_clone) { - crm_config_err("Invalid constraint '%s':" - " Resource '%s' is not a clone but instance %s was requested", - id, id_then, instance_then); - return FALSE; + } else if(instance_then && rsc_then->variant < pe_clone) { + crm_config_err("Invalid constraint '%s':" + " Resource '%s' is not a clone but instance %s was requested", + id, id_then, instance_then); + return FALSE; - } else if(instance_first && rsc_first->variant < pe_clone) { - crm_config_err("Invalid constraint '%s':" - " Resource '%s' is not a clone but instance %s was requested", - id, id_first, instance_first); - return FALSE; - } + } else if(instance_first && rsc_first->variant < pe_clone) { + crm_config_err("Invalid constraint '%s':" + " Resource '%s' is not a clone but instance %s was requested", + id, id_first, instance_first); + return FALSE; + } - if(instance_then) { - rsc_then = find_clone_instance(rsc_then, instance_then, data_set); - if(rsc_then == NULL) { - crm_config_warn("Invalid constraint '%s': No instance '%s' of '%s'", id, instance_then, id_then); - return FALSE; - } + if(instance_then) { + rsc_then = find_clone_instance(rsc_then, instance_then, data_set); + if(rsc_then == NULL) { + crm_config_warn("Invalid constraint '%s': No instance '%s' of '%s'", id, instance_then, id_then); + return FALSE; } + } - if(instance_first) { - rsc_first = find_clone_instance(rsc_first, instance_first, data_set); - if(rsc_first == NULL) { - crm_config_warn("Invalid constraint '%s': No instance '%s' of '%s'", id, instance_first, id_first); - return FALSE; - } + if(instance_first) { + rsc_first = find_clone_instance(rsc_first, instance_first, data_set); + if(rsc_first == NULL) { + crm_config_warn("Invalid constraint '%s': No instance '%s' of '%s'", id, instance_first, id_first); + return FALSE; } + } - cons_weight = pe_order_optional; - kind = get_ordering_type(xml_obj); + cons_weight = pe_order_optional; + kind = get_ordering_type(xml_obj); - if(kind == pe_order_kind_optional && rsc_then->restart_type == pe_restart_restart) { - crm_debug_2("Upgrade : recovery - implies right"); - cons_weight |= pe_order_implies_then; - } + if(kind == pe_order_kind_optional && rsc_then->restart_type == pe_restart_restart) { + crm_debug_2("Upgrade : recovery - implies right"); + cons_weight |= pe_order_implies_then; + } - cons_weight |= get_flags(id, kind, action_first, action_then, FALSE); - order_id = new_rsc_order( - rsc_first, action_first, rsc_then, action_then, cons_weight, data_set); + cons_weight |= get_flags(id, kind, action_first, action_then, FALSE); + order_id = new_rsc_order( + rsc_first, action_first, rsc_then, action_then, cons_weight, data_set); - crm_debug_2("order-%d (%s): %s_%s before %s_%s flags=0x%.6x", - order_id, id, rsc_first->id, action_first, rsc_then->id, action_then, - cons_weight); + crm_debug_2("order-%d (%s): %s_%s before %s_%s flags=0x%.6x", + order_id, id, rsc_first->id, action_first, rsc_then->id, action_then, + cons_weight); - if(invert_bool == FALSE) { - return TRUE; + if(invert_bool == FALSE) { + return TRUE; - } else if(invert && kind == pe_order_kind_serialize) { - crm_config_warn("Cannot invert serialized constraint set %s", id); - return TRUE; + } else if(invert && kind == pe_order_kind_serialize) { + crm_config_warn("Cannot invert serialized constraint set %s", id); + return TRUE; - } else if(kind == pe_order_kind_serialize) { - return TRUE; - } + } else if(kind == pe_order_kind_serialize) { + return TRUE; + } - action_then = invert_action(action_then); - action_first = invert_action(action_first); - if(action_then == NULL || action_first == NULL) { - crm_config_err("Cannot invert rsc_order constraint %s." - " Please specify the inverse manually.", id); - return TRUE; - } + action_then = invert_action(action_then); + action_first = invert_action(action_first); + if(action_then == NULL || action_first == NULL) { + crm_config_err("Cannot invert rsc_order constraint %s." + " Please specify the inverse manually.", id); + return TRUE; + } - cons_weight = pe_order_optional; - if(kind == pe_order_kind_optional && rsc_then->restart_type == pe_restart_restart) { - crm_debug_2("Upgrade : recovery - implies left"); - cons_weight |= pe_order_implies_first; - } + cons_weight = pe_order_optional; + if(kind == pe_order_kind_optional && rsc_then->restart_type == pe_restart_restart) { + crm_debug_2("Upgrade : recovery - implies left"); + cons_weight |= pe_order_implies_first; + } - cons_weight |= get_flags(id, kind, action_first, action_then, TRUE); - order_id = new_rsc_order( - rsc_then, action_then, rsc_first, action_first, cons_weight, data_set); + cons_weight |= get_flags(id, kind, action_first, action_then, TRUE); + order_id = new_rsc_order( + rsc_then, action_then, rsc_first, action_first, cons_weight, data_set); - crm_debug_2("order-%d (%s): %s_%s before %s_%s flags=0x%.6x", - order_id, id, rsc_then->id, action_then, rsc_first->id, action_first, - cons_weight); + crm_debug_2("order-%d (%s): %s_%s before %s_%s flags=0x%.6x", + order_id, id, rsc_then->id, action_then, rsc_first->id, action_first, + cons_weight); - return TRUE; + return TRUE; } gboolean unpack_rsc_location(xmlNode * xml_obj, pe_working_set_t *data_set) { - gboolean empty = TRUE; - rsc_to_node_t *location = NULL; - const char *id_lh = crm_element_value(xml_obj, "rsc"); - const char *id = crm_element_value(xml_obj, XML_ATTR_ID); - resource_t *rsc_lh = pe_find_resource(data_set->resources, id_lh); - const char *node = crm_element_value(xml_obj, "node"); - const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE); - const char *domain = crm_element_value(xml_obj, XML_CIB_TAG_DOMAIN); - const char *role = crm_element_value(xml_obj, XML_RULE_ATTR_ROLE); + gboolean empty = TRUE; + rsc_to_node_t *location = NULL; + const char *id_lh = crm_element_value(xml_obj, "rsc"); + const char *id = crm_element_value(xml_obj, XML_ATTR_ID); + resource_t *rsc_lh = pe_find_resource(data_set->resources, id_lh); + const char *node = crm_element_value(xml_obj, "node"); + const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE); + const char *domain = crm_element_value(xml_obj, XML_CIB_TAG_DOMAIN); + const char *role = crm_element_value(xml_obj, XML_RULE_ATTR_ROLE); - if(rsc_lh == NULL) { - /* only a warn as BSC adds the constraint then the resource */ - crm_config_warn("No resource (con=%s, rsc=%s)", id, id_lh); - return FALSE; - } + if(rsc_lh == NULL) { + /* only a warn as BSC adds the constraint then the resource */ + crm_config_warn("No resource (con=%s, rsc=%s)", id, id_lh); + return FALSE; + } - if(domain) { - GListPtr nodes = g_hash_table_lookup(data_set->domains, domain); + if(domain) { + GListPtr nodes = g_hash_table_lookup(data_set->domains, domain); - if(domain == NULL) { - crm_config_err("Invalid constraint %s: Domain %s does not exist", - id, domain); - return FALSE; - } + if(domain == NULL) { + crm_config_err("Invalid constraint %s: Domain %s does not exist", + id, domain); + return FALSE; + } - location = rsc2node_new(id, rsc_lh, 0, NULL, data_set); - location->node_list_rh = node_list_dup(nodes, FALSE, FALSE); + location = rsc2node_new(id, rsc_lh, 0, NULL, data_set); + location->node_list_rh = node_list_dup(nodes, FALSE, FALSE); - } else if(node != NULL && score != NULL) { - int score_i = char2score(score); - node_t *match = pe_find_node(data_set->nodes, node); + } else if(node != NULL && score != NULL) { + int score_i = char2score(score); + node_t *match = pe_find_node(data_set->nodes, node); - if(!match) { - return FALSE; - } - location = rsc2node_new(id, rsc_lh, score_i, match, data_set); + if(!match) { + return FALSE; + } + location = rsc2node_new(id, rsc_lh, score_i, match, data_set); - } else { - xml_child_iter_filter( - xml_obj, rule_xml, XML_TAG_RULE, + } else { + xmlNode *rule_xml = NULL; + for(rule_xml = xml_obj; rule_xml != NULL; rule_xml = rule_xml->next) { + if(crm_str_eq((const char *)rule_xml->name, XML_TAG_RULE, TRUE)) { empty = FALSE; crm_debug_2("Unpacking %s/%s", id, ID(rule_xml)); generate_location_rule(rsc_lh, rule_xml, data_set); - ); - - if(empty) { - crm_config_err("Invalid location constraint %s:" - " rsc_location must contain at least one rule", - ID(xml_obj)); } } + + if(empty) { + crm_config_err("Invalid location constraint %s:" + " rsc_location must contain at least one rule", + ID(xml_obj)); + } + } - if(location && role) { - if(text2role(role) == RSC_ROLE_UNKNOWN) { - pe_err("Invalid constraint %s: Bad role %s", id, role); - return FALSE; + if(location && role) { + if(text2role(role) == RSC_ROLE_UNKNOWN) { + pe_err("Invalid constraint %s: Bad role %s", id, role); + return FALSE; - } else { - location->role_filter = text2role(role); - if(location->role_filter == RSC_ROLE_SLAVE) { - /* Fold slave back into Started for simplicity - * At the point Slave location constraints are evaluated, - * all resources are still either stopped or started - */ - location->role_filter = RSC_ROLE_STARTED; - } + } else { + location->role_filter = text2role(role); + if(location->role_filter == RSC_ROLE_SLAVE) { + /* Fold slave back into Started for simplicity + * At the point Slave location constraints are evaluated, + * all resources are still either stopped or started + */ + location->role_filter = RSC_ROLE_STARTED; } } - return TRUE; + } + 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); + 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 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 { + 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); - } + } 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; + } + 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 gIter = NULL; GListPtr match_L = NULL; int score_f = 0; gboolean do_and = TRUE; gboolean accept = TRUE; gboolean raw_score = TRUE; rsc_to_node_t *location_rule = NULL; rule_xml = expand_idref(rule_xml, data_set->input); rule_id = crm_element_value(rule_xml, XML_ATTR_ID); boolean = crm_element_value(rule_xml, XML_RULE_ATTR_BOOLEAN_OP); role = crm_element_value(rule_xml, XML_RULE_ATTR_ROLE); crm_debug_2("Processing rule: %s", rule_id); if(role != NULL && text2role(role) == RSC_ROLE_UNKNOWN) { pe_err("Bad role specified for %s: %s", rule_id, role); return NULL; } score = crm_element_value(rule_xml, XML_RULE_ATTR_SCORE); if(score != NULL) { score_f = char2score(score); } else { score = crm_element_value( rule_xml, XML_RULE_ATTR_SCORE_ATTRIBUTE); if(score == NULL) { score = crm_element_value( rule_xml, XML_RULE_ATTR_SCORE_MANGLED); } if(score != NULL) { raw_score = FALSE; } } if(safe_str_eq(boolean, "or")) { do_and = FALSE; } location_rule = rsc2node_new(rule_id, rsc, 0, NULL, data_set); if(location_rule == NULL) { return NULL; } if(role != NULL) { crm_debug_2("Setting role filter: %s", role); location_rule->role_filter = text2role(role); if(location_rule->role_filter == RSC_ROLE_SLAVE) { /* Fold slave back into Started for simplicity * At the point Slave location constraints are evaluated, * all resources are still either stopped or started */ location_rule->role_filter = RSC_ROLE_STARTED; } } if(do_and) { GListPtr gIter = NULL; match_L = node_list_dup(data_set->nodes, TRUE, FALSE); gIter = match_L; for(; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t*)gIter->data; node->weight = get_node_score(rule_id, score, raw_score, node); } } gIter = data_set->nodes; for(; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t*)gIter->data; accept = test_rule( rule_xml, node->details->attrs, RSC_ROLE_UNKNOWN, data_set->now); crm_debug_2("Rule %s %s on %s", ID(rule_xml), accept?"passed":"failed", node->details->uname); score_f = get_node_score(rule_id, score, raw_score, node); /* if(accept && score_f == -INFINITY) { */ /* accept = FALSE; */ /* } */ if(accept) { node_t *local = pe_find_node_id(match_L, node->details->id); if(local == NULL && do_and) { continue; } else if(local == NULL) { local = node_copy(node); match_L = g_list_append(match_L, local); } if(do_and == FALSE) { local->weight = merge_weights(local->weight, score_f); } crm_debug_2("node %s now has weight %d", node->details->uname, local->weight); } else if(do_and && !accept) { /* remove it */ node_t *delete = pe_find_node_id(match_L, node->details->id); if(delete != NULL) { match_L = g_list_remove(match_L,delete); crm_debug_5("node %s did not match", node->details->uname); } crm_free(delete); } } location_rule->node_list_rh = match_L; if(location_rule->node_list_rh == NULL) { crm_debug_2("No matching nodes for rule %s", rule_id); return NULL; } crm_debug_3("%s: %d nodes matched", rule_id, g_list_length(location_rule->node_list_rh)); return location_rule; } static gint sort_cons_priority_lh(gconstpointer a, gconstpointer b) { const rsc_colocation_t *rsc_constraint1 = (const rsc_colocation_t*)a; const rsc_colocation_t *rsc_constraint2 = (const rsc_colocation_t*)b; if(a == NULL) { return 1; } if(b == NULL) { return -1; } CRM_ASSERT(rsc_constraint1->rsc_lh != NULL); CRM_ASSERT(rsc_constraint1->rsc_rh != NULL); if(rsc_constraint1->rsc_lh->priority > rsc_constraint2->rsc_lh->priority) { return -1; } if(rsc_constraint1->rsc_lh->priority < rsc_constraint2->rsc_lh->priority) { return 1; } return strcmp(rsc_constraint1->rsc_lh->id, rsc_constraint2->rsc_lh->id); } static gint sort_cons_priority_rh(gconstpointer a, gconstpointer b) { const rsc_colocation_t *rsc_constraint1 = (const rsc_colocation_t*)a; const rsc_colocation_t *rsc_constraint2 = (const rsc_colocation_t*)b; if(a == NULL) { return 1; } if(b == NULL) { return -1; } CRM_ASSERT(rsc_constraint1->rsc_lh != NULL); CRM_ASSERT(rsc_constraint1->rsc_rh != NULL); if(rsc_constraint1->rsc_rh->priority > rsc_constraint2->rsc_rh->priority) { return -1; } if(rsc_constraint1->rsc_rh->priority < rsc_constraint2->rsc_rh->priority) { return 1; } return strcmp(rsc_constraint1->rsc_rh->id, rsc_constraint2->rsc_rh->id); } gboolean rsc_colocation_new(const char *id, const char *node_attr, int score, resource_t *rsc_lh, resource_t *rsc_rh, const char *state_lh, const char *state_rh, pe_working_set_t *data_set) { rsc_colocation_t *new_con = NULL; if(rsc_lh == NULL){ crm_config_err("No resource found for LHS %s", id); return FALSE; } else if(rsc_rh == NULL){ crm_config_err("No resource found for RHS of %s", id); return FALSE; } crm_malloc0(new_con, sizeof(rsc_colocation_t)); if(new_con == NULL) { return FALSE; } if(state_lh == NULL || safe_str_eq(state_lh, RSC_ROLE_STARTED_S)) { state_lh = RSC_ROLE_UNKNOWN_S; } if(state_rh == NULL || safe_str_eq(state_rh, RSC_ROLE_STARTED_S)) { state_rh = RSC_ROLE_UNKNOWN_S; } new_con->id = id; new_con->rsc_lh = rsc_lh; new_con->rsc_rh = rsc_rh; new_con->score = score; new_con->role_lh = text2role(state_lh); new_con->role_rh = text2role(state_rh); new_con->node_attribute = node_attr; if(node_attr == NULL) { node_attr = "#"XML_ATTR_UNAME; } crm_debug_3("%s ==> %s (%s %d)", rsc_lh->id, rsc_rh->id, node_attr, score); rsc_lh->rsc_cons = g_list_insert_sorted( rsc_lh->rsc_cons, new_con, sort_cons_priority_rh); rsc_rh->rsc_cons_lhs = g_list_insert_sorted( rsc_rh->rsc_cons_lhs, new_con, sort_cons_priority_lh); data_set->colocation_constraints = g_list_append( data_set->colocation_constraints, new_con); return TRUE; } /* LHS before RHS */ int new_rsc_order(resource_t *lh_rsc, const char *lh_task, resource_t *rh_rsc, const char *rh_task, enum pe_ordering type, pe_working_set_t *data_set) { char *lh_key = NULL; char *rh_key = NULL; CRM_CHECK(lh_rsc != NULL, return -1); CRM_CHECK(lh_task != NULL, return -1); CRM_CHECK(rh_rsc != NULL, return -1); CRM_CHECK(rh_task != NULL, return -1); lh_key = generate_op_key(lh_rsc->id, lh_task, 0); rh_key = generate_op_key(rh_rsc->id, rh_task, 0); return custom_action_order(lh_rsc, lh_key, NULL, rh_rsc, rh_key, NULL, type, data_set); } /* LHS before RHS */ int custom_action_order( - resource_t *lh_rsc, char *lh_action_task, action_t *lh_action, - resource_t *rh_rsc, char *rh_action_task, action_t *rh_action, - enum pe_ordering type, pe_working_set_t *data_set) + 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; - } + 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; - } + 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)); - - 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; + crm_malloc0(order, sizeof(order_constraint_t)); + + 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_prepend( - data_set->ordering_constraints, order); + data_set->ordering_constraints = g_list_prepend( + data_set->ordering_constraints, order); - return order->id; + return order->id; } enum pe_ordering get_flags( const char *id, enum pe_order_kind kind, const char *action_first, const char *action_then, gboolean invert) { enum pe_ordering flags = pe_order_optional; if(invert && kind == pe_order_kind_mandatory) { crm_debug_2("Upgrade %s: implies left", id); flags |= pe_order_implies_first; } else if(kind == pe_order_kind_mandatory) { crm_debug_2("Upgrade %s: implies right", id); flags |= pe_order_implies_then; if(safe_str_eq(action_first, RSC_START) || safe_str_eq(action_first, RSC_PROMOTE)) { crm_debug_2("Upgrade %s: runnable", id); flags |= pe_order_runnable_left; } } else if(kind == pe_order_kind_serialize) { flags |= pe_order_serialize_only; } return flags; } static gboolean unpack_order_set(xmlNode *set, enum pe_order_kind kind, resource_t **rsc, action_t **begin, action_t **end, action_t **inv_begin, action_t **inv_end, const char *symmetrical, pe_working_set_t *data_set) { + xmlNode *xml_rsc = NULL; GListPtr set_iter = NULL; GListPtr resources = NULL; resource_t *last = NULL; resource_t *resource = NULL; int local_kind = kind; gboolean sequential = FALSE; enum pe_ordering flags = pe_order_optional; char *key = NULL; const char *id = ID(set); const char *action = crm_element_value(set, "action"); const char *sequential_s = crm_element_value(set, "sequential"); const char *kind_s = crm_element_value(set, XML_ORDER_ATTR_KIND); /* - char *pseudo_id = NULL; - char *end_id = NULL; - char *begin_id = NULL; + char *pseudo_id = NULL; + char *end_id = NULL; + char *begin_id = NULL; */ if(action == NULL) { action = RSC_START; } if(kind_s) { local_kind = get_ordering_type(set); } if(sequential_s == NULL) { sequential_s = "1"; } sequential = crm_is_true(sequential_s); flags = get_flags(id, local_kind, action, action, FALSE); - xml_child_iter_filter( - set, xml_rsc, XML_TAG_RESOURCE_REF, - - resource = pe_find_resource(data_set->resources, ID(xml_rsc)); - resources = g_list_append(resources, resource); - ); + for(xml_rsc = set; xml_rsc != NULL; xml_rsc = xml_rsc->next) { + if(crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) { + resource = pe_find_resource(data_set->resources, ID(xml_rsc)); + resources = g_list_append(resources, resource); + } + } if(g_list_length(resources) == 1) { crm_debug_2("Single set: %s", id); *rsc = resource; *end = NULL; *begin = NULL; *inv_end = NULL; *inv_begin = NULL; goto done; } /* - pseudo_id = crm_concat(id, action, '-'); - end_id = crm_concat(pseudo_id, "end", '-'); - begin_id = crm_concat(pseudo_id, "begin", '-'); + pseudo_id = crm_concat(id, action, '-'); + end_id = crm_concat(pseudo_id, "end", '-'); + begin_id = crm_concat(pseudo_id, "begin", '-'); */ *rsc = NULL; /* - *end = get_pseudo_op(end_id, data_set); - *begin = get_pseudo_op(begin_id, data_set); + *end = get_pseudo_op(end_id, data_set); + *begin = get_pseudo_op(begin_id, data_set); - crm_free(pseudo_id); - crm_free(begin_id); - crm_free(end_id); + crm_free(pseudo_id); + crm_free(begin_id); + crm_free(end_id); */ set_iter = resources; while(set_iter != NULL) { resource = (resource_t *) set_iter->data; set_iter = set_iter->next; key = generate_op_key(resource->id, action, 0); /* - custom_action_order(NULL, NULL, *begin, resource, crm_strdup(key), NULL, - flags|pe_order_implies_first_printed, data_set); + custom_action_order(NULL, NULL, *begin, resource, crm_strdup(key), NULL, + flags|pe_order_implies_first_printed, data_set); - custom_action_order(resource, crm_strdup(key), NULL, NULL, NULL, *end, - flags|pe_order_implies_then_printed, data_set); + custom_action_order(resource, crm_strdup(key), NULL, NULL, NULL, *end, + flags|pe_order_implies_then_printed, data_set); */ if(local_kind == pe_order_kind_serialize) { /* Serialize before everything that comes after */ GListPtr gIter = NULL; gIter = set_iter; for(; gIter != NULL; gIter = gIter->next) { resource_t *then_rsc = (resource_t*)gIter->data; char *then_key = generate_op_key(then_rsc->id, action, 0); custom_action_order(resource, crm_strdup(key), NULL, then_rsc, then_key, NULL, flags, data_set); } } else if(sequential) { if(last != NULL) { new_rsc_order(last, action, resource, action, flags, data_set); } last = resource; } crm_free(key); } if(crm_is_true(symmetrical) == FALSE) { goto done; } else if(symmetrical && local_kind == pe_order_kind_serialize) { crm_config_warn("Cannot invert serialized constraint set %s", id); goto done; } else if(local_kind == pe_order_kind_serialize) { goto done; } last = NULL; action = invert_action(action); /* - pseudo_id = crm_concat(id, action, '-'); - end_id = crm_concat(pseudo_id, "end", '-'); - begin_id = crm_concat(pseudo_id, "begin", '-'); + pseudo_id = crm_concat(id, action, '-'); + end_id = crm_concat(pseudo_id, "end", '-'); + begin_id = crm_concat(pseudo_id, "begin", '-'); - *inv_end = get_pseudo_op(end_id, data_set); - *inv_begin = get_pseudo_op(begin_id, data_set); + *inv_end = get_pseudo_op(end_id, data_set); + *inv_begin = get_pseudo_op(begin_id, data_set); - crm_free(pseudo_id); - crm_free(begin_id); - crm_free(end_id); + crm_free(pseudo_id); + crm_free(begin_id); + crm_free(end_id); */ flags = get_flags(id, local_kind, action, action, TRUE); set_iter = resources; while(set_iter != NULL) { resource = (resource_t *) set_iter->data; set_iter = set_iter->next; /* - key = generate_op_key(resource->id, action, 0); + key = generate_op_key(resource->id, action, 0); - custom_action_order(NULL, NULL, *inv_begin, resource, crm_strdup(key), NULL, - flags|pe_order_implies_first_printed, data_set); + custom_action_order(NULL, NULL, *inv_begin, resource, crm_strdup(key), NULL, + flags|pe_order_implies_first_printed, data_set); - custom_action_order(resource, key, NULL, NULL, NULL, *inv_end, - flags|pe_order_implies_then_printed, data_set); + custom_action_order(resource, key, NULL, NULL, NULL, *inv_end, + flags|pe_order_implies_then_printed, data_set); */ if(sequential) { if(last != NULL) { new_rsc_order(resource, action, last, action, flags, data_set); } last = resource; } } done: g_list_free(resources); return TRUE; } static gboolean order_rsc_sets( const char *id, xmlNode *set1, xmlNode *set2, enum pe_order_kind kind, pe_working_set_t *data_set, gboolean invert) { + xmlNode *xml_rsc = 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 = pe_order_none; if (action_1 == NULL) { action_1 = RSC_START; }; if (action_2 == NULL) { action_2 = RSC_START; }; if (invert == FALSE) { flags = get_flags(id, kind, action_1, action_2, FALSE); } else { action_1 = invert_action(action_1); action_2 = invert_action(action_2); flags = get_flags(id, kind, action_2, action_1, TRUE); } if(crm_is_true(sequential_1)) { if(invert == FALSE) { /* get the last one */ const char *rid = NULL; - xml_child_iter_filter( - set1, xml_rsc, XML_TAG_RESOURCE_REF, - rid = ID(xml_rsc); - ); + for(xml_rsc = set1; xml_rsc != NULL; xml_rsc = xml_rsc->next) { + if(crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) { + rid = ID(xml_rsc); + } + } rsc_1 = pe_find_resource(data_set->resources, rid); } else { /* get the first one */ - xml_child_iter_filter( - set1, xml_rsc, XML_TAG_RESOURCE_REF, - rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc)); - break; - ); + for(xml_rsc = set1; xml_rsc != NULL; xml_rsc = xml_rsc->next) { + if(crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) { + rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc)); + break; + } + } } } if(crm_is_true(sequential_2)) { if(invert == FALSE) { /* get the first one */ - xml_child_iter_filter( - set2, xml_rsc, XML_TAG_RESOURCE_REF, - rsc_2 = pe_find_resource(data_set->resources, ID(xml_rsc)); - break; - ); + for(xml_rsc = set2; xml_rsc != NULL; xml_rsc = xml_rsc->next) { + if(crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) { + rsc_2 = pe_find_resource(data_set->resources, ID(xml_rsc)); + break; + } + } + } else { /* get the last one */ const char *rid = NULL; - xml_child_iter_filter( - set2, xml_rsc, XML_TAG_RESOURCE_REF, - rid = ID(xml_rsc); - ); + for(xml_rsc = set2; xml_rsc != NULL; xml_rsc = xml_rsc->next) { + if(crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) { + rid = ID(xml_rsc); + } + } rsc_2 = pe_find_resource(data_set->resources, rid); } } if(rsc_1 != NULL && rsc_2 != NULL) { new_rsc_order(rsc_1, action_1, rsc_2, action_2, flags, data_set); } else if(rsc_1 != NULL) { - xml_child_iter_filter( - set2, xml_rsc, XML_TAG_RESOURCE_REF, - rsc_2 = pe_find_resource(data_set->resources, ID(xml_rsc)); - new_rsc_order(rsc_1, action_1, rsc_2, action_2, flags, data_set); - ); + for(xml_rsc = set2; xml_rsc != NULL; xml_rsc = xml_rsc->next) { + if(crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) { + rsc_2 = pe_find_resource(data_set->resources, ID(xml_rsc)); + new_rsc_order(rsc_1, action_1, rsc_2, action_2, flags, data_set); + } + } } else if(rsc_2 != NULL) { - xml_child_iter_filter( - set1, xml_rsc, XML_TAG_RESOURCE_REF, - rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc)); - new_rsc_order(rsc_1, action_1, rsc_2, action_2, flags, data_set); - ); + xmlNode *xml_rsc = NULL; + for(xml_rsc = set1; xml_rsc != NULL; xml_rsc = xml_rsc->next) { + if(crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) { + rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc)); + new_rsc_order(rsc_1, action_1, rsc_2, action_2, flags, data_set); + } + } } else { - xml_child_iter_filter( - set1, xml_rsc, XML_TAG_RESOURCE_REF, - rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc)); + for(xml_rsc = set1; xml_rsc != NULL; xml_rsc = xml_rsc->next) { + if(crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) { + xmlNode *xml_rsc_2 = NULL; + rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc)); - xml_child_iter_filter( - set2, xml_rsc_2, XML_TAG_RESOURCE_REF, - rsc_2 = pe_find_resource(data_set->resources, ID(xml_rsc_2)); - new_rsc_order(rsc_1, action_1, rsc_2, action_2, flags, data_set); - ); - ); + for(xml_rsc_2 = set2; xml_rsc_2 != NULL; xml_rsc_2 = xml_rsc_2->next) { + if(crm_str_eq((const char *)xml_rsc_2->name, XML_TAG_RESOURCE_REF, TRUE)) { + + rsc_2 = pe_find_resource(data_set->resources, ID(xml_rsc_2)); + new_rsc_order(rsc_1, action_1, rsc_2, action_2, flags, data_set); + } + } + } + } } return TRUE; } -/* -static char *null_or_opkey(resource_t *rsc, const char *action) -{ - if(rsc == NULL) { - return NULL; - } - return generate_op_key(rsc->id, action, 0); -} -*/ - gboolean unpack_rsc_order(xmlNode *xml_obj, pe_working_set_t *data_set) { - gboolean any_sets = FALSE; + gboolean any_sets = FALSE; - resource_t *rsc = NULL; - /* - resource_t *last_rsc = NULL; - */ + resource_t *rsc = NULL; + /* + resource_t *last_rsc = NULL; + */ - action_t *set_end = NULL; - action_t *set_begin = NULL; + action_t *set_end = NULL; + action_t *set_begin = NULL; - action_t *set_inv_end = NULL; - action_t *set_inv_begin = NULL; + action_t *set_inv_end = NULL; + action_t *set_inv_begin = NULL; - xmlNode *last = NULL; - /* - action_t *last_end = NULL; - action_t *last_begin = NULL; - action_t *last_inv_end = NULL; - action_t *last_inv_begin = NULL; - */ + xmlNode *set = NULL; + xmlNode *last = NULL; + /* + action_t *last_end = NULL; + action_t *last_begin = NULL; + action_t *last_inv_end = NULL; + action_t *last_inv_begin = NULL; + */ - const char *id = crm_element_value(xml_obj, XML_ATTR_ID); - const char *invert = crm_element_value(xml_obj, XML_CONS_ATTR_SYMMETRICAL); - enum pe_order_kind kind = get_ordering_type(xml_obj); + const char *id = crm_element_value(xml_obj, XML_ATTR_ID); + const char *invert = crm_element_value(xml_obj, XML_CONS_ATTR_SYMMETRICAL); + enum pe_order_kind kind = get_ordering_type(xml_obj); - if(invert == NULL) { - invert = "true"; - } - - xml_child_iter_filter( - xml_obj, set, XML_CONS_TAG_RSC_SET, + if(invert == NULL) { + invert = "true"; + } + for(set = xml_obj; set != NULL; set = set->next) { + if(crm_str_eq((const char *)set->name, XML_CONS_TAG_RSC_SET, TRUE)) { any_sets = TRUE; set = expand_idref(set, data_set->input); if(unpack_order_set(set, kind, &rsc, &set_begin, &set_end, &set_inv_begin, &set_inv_end, invert, data_set) == FALSE) { return FALSE; - /* Expand orders in order_rsc_sets() instead of via pseudo actions. */ - /* - } else if(last) { - const char *set_action = crm_element_value(set, "action"); - const char *last_action = crm_element_value(last, "action"); - enum pe_ordering flags = get_flags(id, kind, last_action, set_action, FALSE); + /* Expand orders in order_rsc_sets() instead of via pseudo actions. */ + /* + } else if(last) { + const char *set_action = crm_element_value(set, "action"); + const char *last_action = crm_element_value(last, "action"); + enum pe_ordering flags = get_flags(id, kind, last_action, set_action, FALSE); - if(!set_action) { set_action = RSC_START; } - if(!last_action) { last_action = RSC_START; } + if(!set_action) { set_action = RSC_START; } + if(!last_action) { last_action = RSC_START; } - if(rsc == NULL && last_rsc == NULL) { - order_actions(last_end, set_begin, flags); - } else { - custom_action_order( - last_rsc, null_or_opkey(last_rsc, last_action), last_end, - rsc, null_or_opkey(rsc, set_action), set_begin, - flags, data_set); - } + if(rsc == NULL && last_rsc == NULL) { + order_actions(last_end, set_begin, flags); + } else { + custom_action_order( + last_rsc, null_or_opkey(last_rsc, last_action), last_end, + rsc, null_or_opkey(rsc, set_action), set_begin, + flags, data_set); + } - if(crm_is_true(invert)) { - set_action = invert_action(set_action); - last_action = invert_action(last_action); + if(crm_is_true(invert)) { + set_action = invert_action(set_action); + last_action = invert_action(last_action); - flags = get_flags(id, kind, last_action, set_action, TRUE); - if(rsc == NULL && last_rsc == NULL) { - order_actions(last_inv_begin, set_inv_end, flags); - - } else { - custom_action_order( - last_rsc, null_or_opkey(last_rsc, last_action), last_inv_begin, - rsc, null_or_opkey(rsc, set_action), set_inv_end, - flags, data_set); - } - } - */ + flags = get_flags(id, kind, last_action, set_action, TRUE); + if(rsc == NULL && last_rsc == NULL) { + order_actions(last_inv_begin, set_inv_end, flags); + + } else { + custom_action_order( + last_rsc, null_or_opkey(last_rsc, last_action), last_inv_begin, + rsc, null_or_opkey(rsc, set_action), set_inv_end, + flags, data_set); + } + } + */ } else if(/* never called -- Now call it for supporting clones in resource sets */last) { if(order_rsc_sets(id, last, set, kind, data_set, FALSE) == FALSE) { - return FALSE; + return FALSE; } if(crm_is_true(invert) && order_rsc_sets(id, set, last, kind, data_set, TRUE) == FALSE) { - return FALSE; + return FALSE; } - + } last = set; /* - last_rsc = rsc; - last_end = set_end; - last_begin = set_begin; - last_inv_end = set_inv_end; - last_inv_begin = set_inv_begin; + last_rsc = rsc; + last_end = set_end; + last_begin = set_begin; + last_inv_end = set_inv_end; + last_inv_begin = set_inv_begin; */ - ); - - if(any_sets == FALSE) { - return unpack_simple_rsc_order(xml_obj, data_set); } + } - return TRUE; + if(any_sets == FALSE) { + return unpack_simple_rsc_order(xml_obj, data_set); + } + + return TRUE; } static gboolean unpack_colocation_set(xmlNode *set, int score, pe_working_set_t *data_set) { + xmlNode *xml_rsc = NULL; resource_t *with = NULL; resource_t *resource = NULL; const char *set_id = ID(set); const char *role = crm_element_value(set, "role"); const char *sequential = crm_element_value(set, "sequential"); int local_score = score; const char *score_s = crm_element_value(set, XML_RULE_ATTR_SCORE); if(score_s) { local_score = char2score(score_s); } if(sequential != NULL && crm_is_true(sequential) == FALSE) { return TRUE; } else if(local_score >= 0) { - xml_child_iter_filter( - set, xml_rsc, XML_TAG_RESOURCE_REF, - - resource = pe_find_resource(data_set->resources, ID(xml_rsc)); - if(with != NULL) { - crm_debug_2("Colocating %s with %s", resource->id, with->id); - rsc_colocation_new(set_id, NULL, local_score, resource, with, role, role, data_set); + for(xml_rsc = set; xml_rsc != NULL; xml_rsc = xml_rsc->next) { + if(crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) { + resource = pe_find_resource(data_set->resources, ID(xml_rsc)); + if(with != NULL) { + crm_debug_2("Colocating %s with %s", resource->id, with->id); + rsc_colocation_new(set_id, NULL, local_score, resource, with, role, role, data_set); + } + + with = resource; } - - with = resource; - ); + } } else { /* Anti-colocating with every prior resource is * the only way to ensure the intuitive result * (ie. that no-one in the set can run with anyone * else in the set) */ - xml_child_iter_filter( - set, xml_rsc, XML_TAG_RESOURCE_REF, - - resource = pe_find_resource(data_set->resources, ID(xml_rsc)); - - xml_child_iter_filter( - set, xml_rsc_with, XML_TAG_RESOURCE_REF, - if(safe_str_eq(resource->id, ID(xml_rsc_with))) { - break; + for(xml_rsc = set; xml_rsc != NULL; xml_rsc = xml_rsc->next) { + if(crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) { + xmlNode *xml_rsc_with = NULL; + resource = pe_find_resource(data_set->resources, ID(xml_rsc)); + + for(xml_rsc_with = set; xml_rsc_with != NULL; xml_rsc_with = xml_rsc_with->next) { + if(crm_str_eq((const char *)xml_rsc_with->name, XML_TAG_RESOURCE_REF, TRUE)) { + if(safe_str_eq(resource->id, ID(xml_rsc_with))) { + break; + } + with = pe_find_resource(data_set->resources, ID(xml_rsc_with)); + crm_debug_2("Anti-Colocating %s with %s", resource->id, with->id); + rsc_colocation_new(set_id, NULL, local_score, resource, with, role, role, data_set); + } } - with = pe_find_resource(data_set->resources, ID(xml_rsc_with)); - crm_debug_2("Anti-Colocating %s with %s", resource->id, with->id); - rsc_colocation_new(set_id, NULL, local_score, resource, with, role, role, data_set); - ); - ); + } + } } return TRUE; } static gboolean colocate_rsc_sets( const char *id, xmlNode *set1, xmlNode *set2, int score, pe_working_set_t *data_set) { + xmlNode *xml_rsc = 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(crm_is_true(sequential_1)) { /* get the first one */ - xml_child_iter_filter( - set1, xml_rsc, XML_TAG_RESOURCE_REF, - rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc)); - break; - ); + for(xml_rsc = set1; xml_rsc != NULL; xml_rsc = xml_rsc->next) { + if(crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) { + rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc)); + break; + } + } } if(crm_is_true(sequential_2)) { /* get the last one */ const char *rid = NULL; - xml_child_iter_filter( - set2, xml_rsc, XML_TAG_RESOURCE_REF, - rid = ID(xml_rsc); - ); + for(xml_rsc = set2; xml_rsc != NULL; xml_rsc = xml_rsc->next) { + if(crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) { + rid = ID(xml_rsc); + } + } rsc_2 = pe_find_resource(data_set->resources, rid); } if(rsc_1 != NULL && rsc_2 != NULL) { rsc_colocation_new(id, NULL, score, rsc_1, rsc_2, role_1, role_2, data_set); } else if(rsc_1 != NULL) { - xml_child_iter_filter( - set2, xml_rsc, XML_TAG_RESOURCE_REF, - rsc_2 = pe_find_resource(data_set->resources, ID(xml_rsc)); - rsc_colocation_new(id, NULL, score, rsc_1, rsc_2, role_1, role_2, data_set); - ); + for(xml_rsc = set2; xml_rsc != NULL; xml_rsc = xml_rsc->next) { + if(crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) { + rsc_2 = pe_find_resource(data_set->resources, ID(xml_rsc)); + rsc_colocation_new(id, NULL, score, rsc_1, rsc_2, role_1, role_2, data_set); + } + } } else if(rsc_2 != NULL) { - xml_child_iter_filter( - set1, xml_rsc, XML_TAG_RESOURCE_REF, - rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc)); - rsc_colocation_new(id, NULL, score, rsc_1, rsc_2, role_1, role_2, data_set); - ); + for(xml_rsc = set1; xml_rsc != NULL; xml_rsc = xml_rsc->next) { + if(crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) { + rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc)); + rsc_colocation_new(id, NULL, score, rsc_1, rsc_2, role_1, role_2, data_set); + } + } + } else { - xml_child_iter_filter( - set1, xml_rsc, XML_TAG_RESOURCE_REF, - rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc)); - - xml_child_iter_filter( - set2, xml_rsc_2, XML_TAG_RESOURCE_REF, - rsc_2 = pe_find_resource(data_set->resources, ID(xml_rsc_2)); - rsc_colocation_new(id, NULL, score, rsc_1, rsc_2, role_1, role_2, data_set); - ); - ); + for(xml_rsc = set1; xml_rsc != NULL; xml_rsc = xml_rsc->next) { + if(crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) { + xmlNode *xml_rsc_2 = NULL; + rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc)); + + for(xml_rsc_2 = set2; xml_rsc_2 != NULL; xml_rsc_2 = xml_rsc_2->next) { + if(crm_str_eq((const char *)xml_rsc_2->name, XML_TAG_RESOURCE_REF, TRUE)) { + rsc_2 = pe_find_resource(data_set->resources, ID(xml_rsc_2)); + rsc_colocation_new(id, NULL, score, rsc_1, rsc_2, role_1, role_2, data_set); + } + } + } + } } return TRUE; } static gboolean unpack_simple_colocation(xmlNode *xml_obj, pe_working_set_t *data_set) { int score_i = 0; const char *id = crm_element_value(xml_obj, XML_ATTR_ID); const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE); const char *id_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE); const char *id_rh = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET); const char *state_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE); const char *state_rh = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET_ROLE); const char *instance_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_INSTANCE); const char *instance_rh = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET_INSTANCE); const char *attr = crm_element_value(xml_obj, XML_COLOC_ATTR_NODE_ATTR); const char *symmetrical = crm_element_value(xml_obj, XML_CONS_ATTR_SYMMETRICAL); resource_t *rsc_lh = pe_find_resource(data_set->resources, id_lh); resource_t *rsc_rh = pe_find_resource(data_set->resources, id_rh); if(rsc_lh == NULL) { crm_config_err("Invalid constraint '%s': No resource named '%s'", id, id_lh); return FALSE; } else if(rsc_rh == NULL) { crm_config_err("Invalid constraint '%s': No resource named '%s'", id, id_rh); return FALSE; } else if(instance_lh && rsc_lh->variant < pe_clone) { crm_config_err("Invalid constraint '%s': Resource '%s' is not a clone but instance %s was requested", id, id_lh, instance_lh); return FALSE; } else if(instance_rh && rsc_rh->variant < pe_clone) { crm_config_err("Invalid constraint '%s': Resource '%s' is not a clone but instance %s was requested", id, id_rh, instance_rh); return FALSE; } if(instance_lh) { rsc_lh = find_clone_instance(rsc_lh, instance_lh, data_set); if(rsc_lh == NULL) { crm_config_warn("Invalid constraint '%s': No instance '%s' of '%s'", id, instance_lh, id_lh); return FALSE; } } if(instance_rh) { rsc_rh = find_clone_instance(rsc_rh, instance_rh, data_set); if(rsc_rh == NULL) { crm_config_warn("Invalid constraint '%s': No instance '%s' of '%s'", id, instance_rh, id_rh); return FALSE; } } if(crm_is_true(symmetrical)) { crm_config_warn("The %s colocation constraint attribute has been removed." " It didn't do what you think it did anyway.", XML_CONS_ATTR_SYMMETRICAL); } if(score) { score_i = char2score(score); } rsc_colocation_new(id, attr, score_i, rsc_lh, rsc_rh, state_lh, state_rh, data_set); return TRUE; } gboolean unpack_rsc_colocation(xmlNode *xml_obj, pe_working_set_t *data_set) { - int score_i = 0; - xmlNode *last = NULL; - gboolean any_sets = FALSE; + int score_i = 0; + xmlNode *set = NULL; + xmlNode *last = NULL; + gboolean any_sets = FALSE; - const char *id = crm_element_value(xml_obj, XML_ATTR_ID); - const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE); + const char *id = crm_element_value(xml_obj, XML_ATTR_ID); + const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE); - if(score) { - score_i = char2score(score); - } + if(score) { + score_i = char2score(score); + } - xml_child_iter_filter( - xml_obj, set, XML_CONS_TAG_RSC_SET, - + for(set = xml_obj; set != NULL; set = set->next) { + if(crm_str_eq((const char *)set->name, XML_CONS_TAG_RSC_SET, TRUE)) { any_sets = TRUE; set = expand_idref(set, data_set->input); if(unpack_colocation_set(set, score_i, data_set) == FALSE) { return FALSE; } else if(last && colocate_rsc_sets(id, last, set, score_i, data_set) == FALSE) { return FALSE; } last = set; - ); - - - if(any_sets == FALSE) { - return unpack_simple_colocation(xml_obj, data_set); } + } + + if(any_sets == FALSE) { + return unpack_simple_colocation(xml_obj, data_set); + } - return TRUE; + return TRUE; } gboolean is_active(rsc_to_node_t *cons) { - return TRUE; + return TRUE; } diff --git a/pengine/group.c b/pengine/group.c index 0521b55068..398cd5ecdd 100644 --- a/pengine/group.c +++ b/pengine/group.c @@ -1,486 +1,486 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #define VARIANT_GROUP 1 #include node_t * group_color(resource_t *rsc, pe_working_set_t *data_set) { node_t *node = NULL; node_t *group_node = NULL; GListPtr gIter = rsc->children; 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("Dependency 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; group_data->first_child->rsc_cons_lhs = g_list_concat( group_data->first_child->rsc_cons_lhs, rsc->rsc_cons_lhs); rsc->rsc_cons_lhs = NULL; dump_node_scores(show_scores?0:scores_log_level, rsc, __PRETTY_FUNCTION__, rsc->allowed_nodes); for(; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t*)gIter->data; node = child_rsc->cmds->allocate(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; GListPtr gIter = rsc->children; crm_debug_2("Creating actions for %s", rsc->id); for(; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t*)gIter->data; 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 */); set_bit_inplace(op->flags, pe_action_pseudo|pe_action_runnable); op = custom_action(rsc, started_key(rsc), RSC_STARTED, NULL, TRUE/* !group_data->child_starting */, TRUE, data_set); set_bit_inplace(op->flags, pe_action_pseudo|pe_action_runnable); op = stop_action(rsc, NULL, TRUE/* !group_data->child_stopping */); set_bit_inplace(op->flags, pe_action_pseudo|pe_action_runnable); op = custom_action(rsc, stopped_key(rsc), RSC_STOPPED, NULL, TRUE/* !group_data->child_stopping */, TRUE, data_set); set_bit_inplace(op->flags, pe_action_pseudo|pe_action_runnable); value = g_hash_table_lookup(rsc->meta, "stateful"); if(crm_is_true(value)) { op = custom_action(rsc, demote_key(rsc), RSC_DEMOTE, NULL, TRUE, TRUE, data_set); set_bit_inplace(op->flags, pe_action_pseudo); set_bit_inplace(op->flags, pe_action_runnable); op = custom_action(rsc, demoted_key(rsc), RSC_DEMOTED, NULL, TRUE, TRUE, data_set); set_bit_inplace(op->flags, pe_action_pseudo); set_bit_inplace(op->flags, pe_action_runnable); op = custom_action(rsc, promote_key(rsc), RSC_PROMOTE, NULL, TRUE, TRUE, data_set); set_bit_inplace(op->flags, pe_action_pseudo); set_bit_inplace(op->flags, pe_action_runnable); op = custom_action(rsc, promoted_key(rsc), RSC_PROMOTED, NULL, TRUE, TRUE, data_set); set_bit_inplace(op->flags, pe_action_pseudo); set_bit_inplace(op->flags, pe_action_runnable); } } void group_update_pseudo_status(resource_t *parent, resource_t *child) { GListPtr gIter = child->actions; 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; } for(; gIter != NULL; gIter = gIter->next) { action_t *action = (action_t*)gIter->data; if(is_set(action->flags, pe_action_optional)) { continue; } if(safe_str_eq(RSC_STOP, action->task) && is_set(action->flags, pe_action_runnable)) { group_data->child_stopping = TRUE; crm_debug_3("Based on %s the group is stopping", action->uuid); } else if(safe_str_eq(RSC_START, action->task) && is_set(action->flags, pe_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) { GListPtr gIter = rsc->children; resource_t *last_rsc = NULL; resource_t *top = uber_parent(rsc); group_variant_data_t *group_data = NULL; get_group_variant_data(group_data, rsc); new_rsc_order(rsc, RSC_STOPPED, rsc, RSC_START, pe_order_optional, data_set); new_rsc_order(rsc, RSC_START, rsc, RSC_STARTED, pe_order_runnable_left, data_set); new_rsc_order(rsc, RSC_STOP, rsc, RSC_STOPPED, pe_order_runnable_left, data_set); for(; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t*)gIter->data; int stop = pe_order_none; int stopped = pe_order_implies_then_printed; int start = pe_order_implies_then|pe_order_runnable_left; int started = pe_order_runnable_left|pe_order_implies_then|pe_order_implies_then_printed; child_rsc->cmds->internal_constraints(child_rsc, data_set); if(last_rsc == NULL) { if(group_data->ordered) { stop |= pe_order_optional; stopped = pe_order_implies_then; } } else if(group_data->colocated) { rsc_colocation_new( "group:internal_colocation", NULL, INFINITY, child_rsc, last_rsc, NULL, NULL, data_set); } if(top->variant == pe_master) { new_rsc_order(rsc, RSC_DEMOTE, child_rsc, RSC_DEMOTE, stop|pe_order_implies_first_printed, data_set); new_rsc_order(child_rsc, RSC_DEMOTE, rsc, RSC_DEMOTED, stopped, data_set); new_rsc_order(child_rsc, RSC_PROMOTE, rsc, RSC_PROMOTED, started, data_set); new_rsc_order(rsc, RSC_PROMOTE, child_rsc, RSC_PROMOTE, pe_order_implies_first_printed, data_set); } order_start_start(rsc, child_rsc, pe_order_implies_first_printed); order_stop_stop(rsc, child_rsc, stop|pe_order_implies_first_printed); new_rsc_order(child_rsc, RSC_STOP, rsc, RSC_STOPPED, stopped, 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_first_printed); if(top->variant == pe_master) { new_rsc_order(rsc, RSC_PROMOTE, child_rsc, RSC_PROMOTE, start|pe_order_implies_first_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_optional); if(top->variant == pe_master) { 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_optional, data_set); } } else { /* If anyone in the group is starting, then * pe_order_implies_then 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_none; order_start_start(rsc, child_rsc, flags); if(top->variant == pe_master) { 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_then; int stop_stopped_flags = pe_order_optional; order_stop_stop(rsc, last_rsc, stop_stop_flags); new_rsc_order(last_rsc, RSC_STOP, rsc, RSC_STOPPED, stop_stopped_flags, data_set); if(top->variant == pe_master) { 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) { GListPtr gIter = rsc_lh->children; 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; } for(; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t*)gIter->data; 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) { GListPtr gIter = rsc_rh->children; 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) { if(constraint->score >= INFINITY) { /* Ensure RHS is _fully_ up before can start LHS */ group_data->last_child->cmds->rsc_colocation_rh( rsc_lh, group_data->last_child, constraint); } else { /* A partially active RHS is fine */ 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; } for(; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t*)gIter->data; child_rsc->cmds->rsc_colocation_rh(rsc_lh, child_rsc, constraint); } } enum pe_action_flags group_action_flags(action_t *action, node_t *node) { gboolean check_runnable = FALSE; const char *task_s = action->task; GListPtr gIter = action->rsc->children; enum action_tasks task = text2task(task_s); enum pe_action_flags flags = (pe_action_optional | pe_action_runnable | pe_action_pseudo); switch(task) { case stopped_rsc: case started_rsc: case action_demoted: case action_promoted: task_s = task2text(task-1); check_runnable = TRUE; break; default: break; } for(; gIter != NULL; gIter = gIter->next) { resource_t *child = (resource_t*)gIter->data; action_t *child_action = find_first_action(child->actions, NULL, task_s, node); if(child_action) { enum pe_action_flags child_flags = child->cmds->action_flags(child_action, node); if(is_set(flags, pe_action_optional) && is_set(child_flags, pe_action_optional) == FALSE) { crm_trace("%s is manditory because of %s", action->uuid, child_action->uuid); clear_bit_inplace(flags, pe_action_optional); clear_bit_inplace(action->flags, pe_action_optional); } if(check_runnable && is_set(flags, pe_action_runnable) && is_set(child_flags, pe_action_runnable) == FALSE) { crm_trace("%s is not runnable because of %s", action->uuid, child_action->uuid); clear_bit_inplace(flags, pe_action_runnable); clear_bit_inplace(action->flags, pe_action_runnable); } } } return flags; } enum pe_graph_flags group_update_actions( action_t *first, action_t *then, node_t *node, enum pe_action_flags flags, enum pe_action_flags filter, enum pe_ordering type) { GListPtr gIter = then->rsc->children; enum pe_graph_flags changed = pe_graph_none; CRM_ASSERT(then->rsc != NULL); changed |= native_update_actions(first, then, node, flags, filter, type); for(; gIter != NULL; gIter = gIter->next) { resource_t *child = (resource_t*)gIter->data; action_t *child_action = find_first_action(child->actions, NULL, then->task, node); if(child_action) { changed |= child->cmds->update_actions(first, child_action, node, flags, filter, type); } } return changed; } void group_rsc_location(resource_t *rsc, rsc_to_node_t *constraint) { GListPtr gIter = rsc->children; 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); native_rsc_location(rsc, constraint); for(; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t*)gIter->data; child_rsc->cmds->rsc_location(child_rsc, constraint); 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); + slist_basic_destroy(zero); } void group_expand(resource_t *rsc, pe_working_set_t *data_set) { GListPtr gIter = rsc->children; 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); for(; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t*)gIter->data; child_rsc->cmds->expand(child_rsc, data_set); } } GHashTable * group_merge_weights( resource_t *rsc, const char *rhs, GHashTable *nodes, const char *attr, int factor, gboolean allow_rollback, gboolean only_positive) { GListPtr gIter = rsc->rsc_cons_lhs; group_variant_data_t *group_data = NULL; get_group_variant_data(group_data, rsc); if(is_set(rsc->flags, pe_rsc_merging)) { crm_info("Breaking dependency loop with %s at %s", rsc->id, rhs); return nodes; } set_bit(rsc->flags, pe_rsc_merging); nodes = group_data->first_child->cmds->merge_weights( group_data->first_child, rhs, nodes, attr, factor, allow_rollback, only_positive); for(; gIter != NULL; gIter = gIter->next) { rsc_colocation_t *constraint = (rsc_colocation_t*)gIter->data; nodes = rsc_merge_weights( constraint->rsc_lh, rsc->id, nodes, constraint->node_attribute, constraint->score/INFINITY, allow_rollback, only_positive); } clear_bit(rsc->flags, pe_rsc_merging); return nodes; } void group_append_meta(resource_t *rsc, xmlNode *xml) { } diff --git a/pengine/native.c b/pengine/native.c index 656f6676f7..d490e579cf 100644 --- a/pengine/native.c +++ b/pengine/native.c @@ -1,2449 +1,2453 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #define DELETE_THEN_REFRESH 1 /* The crmd will remove the resource from the CIB itself, making this redundant */ #define VARIANT_NATIVE 1 #include void native_rsc_colocation_rh_must(resource_t *rsc_lh, gboolean update_lh, resource_t *rsc_rh, gboolean update_rh); void native_rsc_colocation_rh_mustnot(resource_t *rsc_lh, gboolean update_lh, resource_t *rsc_rh, gboolean update_rh); void Recurring(resource_t *rsc, action_t *start, node_t *node, pe_working_set_t *data_set); void RecurringOp(resource_t *rsc, action_t *start, node_t *node, xmlNode *operation, pe_working_set_t *data_set); void pe_post_notify( resource_t *rsc, node_t *node, action_t *op, notify_data_t *n_data, pe_working_set_t *data_set); void NoRoleChange (resource_t *rsc, node_t *current, node_t *next, pe_working_set_t *data_set); gboolean DeleteRsc (resource_t *rsc, node_t *node, gboolean optional, pe_working_set_t *data_set); gboolean StopRsc (resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set); gboolean StartRsc (resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set); gboolean DemoteRsc (resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set); gboolean PromoteRsc(resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set); gboolean RoleError (resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set); gboolean NullOp (resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set); enum rsc_role_e rsc_state_matrix[RSC_ROLE_MAX][RSC_ROLE_MAX] = { /* Current State */ /* Next State: Unknown Stopped Started Slave Master */ /* Unknown */ { RSC_ROLE_UNKNOWN, RSC_ROLE_STOPPED, RSC_ROLE_STOPPED, RSC_ROLE_STOPPED, RSC_ROLE_STOPPED, }, /* Stopped */ { RSC_ROLE_STOPPED, RSC_ROLE_STOPPED, RSC_ROLE_STARTED, RSC_ROLE_SLAVE, RSC_ROLE_SLAVE, }, /* Started */ { RSC_ROLE_STOPPED, RSC_ROLE_STOPPED, RSC_ROLE_STARTED, RSC_ROLE_SLAVE, RSC_ROLE_MASTER, }, /* Slave */ { RSC_ROLE_STOPPED, RSC_ROLE_STOPPED, RSC_ROLE_UNKNOWN, RSC_ROLE_SLAVE, RSC_ROLE_MASTER, }, /* Master */ { RSC_ROLE_STOPPED, RSC_ROLE_SLAVE, RSC_ROLE_UNKNOWN, RSC_ROLE_SLAVE, RSC_ROLE_MASTER, }, }; gboolean (*rsc_action_matrix[RSC_ROLE_MAX][RSC_ROLE_MAX])(resource_t*,node_t*,gboolean,pe_working_set_t*) = { /* Current State */ /* Next State: Unknown Stopped Started Slave Master */ /* Unknown */ { RoleError, StopRsc, RoleError, RoleError, RoleError, }, /* Stopped */ { RoleError, NullOp, StartRsc, StartRsc, RoleError, }, /* Started */ { RoleError, StopRsc, NullOp, NullOp, PromoteRsc, }, /* Slave */ { RoleError, StopRsc, RoleError, NullOp, PromoteRsc, }, /* Master */ { RoleError, RoleError, RoleError, DemoteRsc, NullOp, }, }; struct capacity_data { node_t *node; resource_t *rsc; gboolean is_enough; }; static void check_capacity(gpointer key, gpointer value, gpointer user_data) { int required = 0; int remaining = 0; struct capacity_data *data = user_data; required = crm_parse_int(value, "0"); remaining = crm_parse_int(g_hash_table_lookup(data->node->details->utilization, key), "0"); if (required > remaining) { crm_debug("Node %s has no enough %s for resource %s: required=%d remaining=%d", data->node->details->uname, (char *)key, data->rsc->id, required, remaining); data->is_enough = FALSE; } } static gboolean have_enough_capacity(node_t *node, resource_t *rsc) { struct capacity_data data; data.node = node; data.rsc = rsc; data.is_enough = TRUE; g_hash_table_foreach(rsc->utilization, check_capacity, &data); return data.is_enough; } static gboolean native_choose_node(resource_t *rsc, pe_working_set_t *data_set) { /* 1. Sort by weight 2. color.chosen_node = the node (of those with the highest wieght) with the fewest resources 3. remove color.chosen_node from all other colors */ int alloc_details = scores_log_level+1; GListPtr nodes = NULL; node_t *chosen = NULL; int lpc = 0; int multiple = 0; int length = 0; gboolean result = FALSE; if (safe_str_neq(data_set->placement_strategy, "default")) { GListPtr gIter = NULL; for(gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t*)gIter->data; if (have_enough_capacity(node, rsc) == FALSE) { crm_debug("Resource %s cannot be allocated to node %s: none of enough capacity", rsc->id, node->details->uname); resource_location(rsc, node, -INFINITY, "__limit_utilization_", data_set); } } dump_node_scores(alloc_details, rsc, "Post-utilization", rsc->allowed_nodes); } length = g_hash_table_size(rsc->allowed_nodes); if(is_not_set(rsc->flags, pe_rsc_provisional)) { return rsc->allocated_to?TRUE:FALSE; } crm_debug_3("Choosing node for %s from %d candidates", rsc->id, length); if(rsc->allowed_nodes) { nodes = g_hash_table_get_values(rsc->allowed_nodes); nodes = g_list_sort_with_data(nodes, sort_node_weight, g_list_nth_data(rsc->running_on, 0)); chosen = g_list_nth_data(nodes, 0); if(chosen && chosen->weight > 0 && can_run_resources(chosen)) { node_t *running = g_list_nth_data(rsc->running_on, 0); if(running && can_run_resources(running) == FALSE) { crm_trace("Current node for %s (%s) can't run resources", rsc->id, running->details->uname); running = NULL; } for(lpc = 1; lpc < length; lpc++) { node_t *tmp = g_list_nth_data(nodes, lpc); if(tmp->weight == chosen->weight) { multiple++; if(running && tmp->details == running->details) { /* prefer the existing node if scores are equal */ chosen = tmp; } } } } } if(multiple > 1) { int log_level = LOG_INFO; char *score = score2char(chosen->weight); if(chosen->weight >= INFINITY) { log_level = LOG_WARNING; } do_crm_log(log_level, "%d nodes with equal score (%s) for" " running %s resources. Chose %s.", multiple, score, rsc->id, chosen->details->uname); crm_free(score); } result = native_assign_node(rsc, nodes, chosen, FALSE); g_list_free(nodes); return result; } static int node_list_attr_score(GHashTable *list, const char *attr, const char *value) { GHashTableIter iter; node_t *node = NULL; int best_score = -INFINITY; const char *best_node = NULL; if(attr == NULL) { attr = "#"XML_ATTR_UNAME; } g_hash_table_iter_init (&iter, list); while (g_hash_table_iter_next (&iter, NULL, (void**)&node)) { int weight = node->weight; if(can_run_resources(node) == FALSE) { weight = -INFINITY; } if(weight > best_score || best_node == NULL) { const char *tmp = g_hash_table_lookup(node->details->attrs, attr); if(safe_str_eq(value, tmp)) { best_score = weight; best_node = node->details->uname; } } } if(safe_str_neq(attr, "#"XML_ATTR_UNAME)) { crm_info("Best score for %s=%s was %s with %d", attr, value, best_node?best_node:"", best_score); } return best_score; } static void node_hash_update(GHashTable *list1, GHashTable *list2, const char *attr, int factor, gboolean only_positive) { int score = 0; int new_score = 0; GHashTableIter iter; node_t *node = NULL; if(attr == NULL) { attr = "#"XML_ATTR_UNAME; } g_hash_table_iter_init (&iter, list1); while (g_hash_table_iter_next (&iter, NULL, (void**)&node)) { CRM_CHECK(node != NULL, continue); score = node_list_attr_score(list2, attr, g_hash_table_lookup(node->details->attrs, attr)); new_score = merge_weights(factor*score, node->weight); if(factor < 0 && score < 0) { /* Negative preference for a node with a negative score * should not become a positive preference * * TODO: Decide if we want to filter only if weight == -INFINITY * */ crm_trace("%s: Filtering %d + %d*%d (factor * score)", node->details->uname, node->weight, factor, score); } else if(only_positive && new_score < 0 && node->weight > 0) { node->weight = 1; crm_trace("%s: Filtering %d + %d*%d (score > 0)", node->details->uname, node->weight, factor, score); } else if(only_positive && new_score < 0 && node->weight == 0) { crm_trace("%s: Filtering %d + %d*%d (score == 0)", node->details->uname, node->weight, factor, score); } else { crm_trace("%s: %d + %d*%d", node->details->uname, node->weight, factor, score); node->weight = new_score; } } } static GHashTable * node_hash_dup(GHashTable *hash) { /* Hack! */ GListPtr list = g_hash_table_get_values(hash); GHashTable *result = node_hash_from_list(list); g_list_free(list); return result; } GHashTable * rsc_merge_weights(resource_t *rsc, const char *rhs, GHashTable *nodes, const char *attr, int factor, gboolean allow_rollback, gboolean only_positive) { GHashTable *work = NULL; int multiplier = 1; if(factor < 0) { multiplier = -1; } if(is_set(rsc->flags, pe_rsc_merging)) { crm_info("%s: Breaking dependency loop at %s", rhs, rsc->id); return nodes; } set_bit(rsc->flags, pe_rsc_merging); crm_debug_2("%s: Combining scores from %s", rhs, rsc->id); work = node_hash_dup(nodes); node_hash_update(work, rsc->allowed_nodes, attr, factor, only_positive); if(allow_rollback && can_run_any(work) == FALSE) { crm_info("%s: Rolling back scores from %s", rhs, rsc->id); g_hash_table_destroy(work); /* TODO: Free memory */ clear_bit(rsc->flags, pe_rsc_merging); return nodes; } if(can_run_any(work)) { GListPtr gIter = NULL; for(gIter = rsc->rsc_cons_lhs; gIter != NULL; gIter = gIter->next) { rsc_colocation_t *constraint = (rsc_colocation_t*)gIter->data; crm_debug_2("Applying %s", constraint->id); work = rsc_merge_weights(constraint->rsc_lh, rhs, work, constraint->node_attribute, multiplier*constraint->score/INFINITY, allow_rollback, only_positive); } } g_hash_table_destroy(nodes); /* TODO: Free memory */ clear_bit(rsc->flags, pe_rsc_merging); return work; } node_t * native_color(resource_t *rsc, pe_working_set_t *data_set) { GListPtr gIter = NULL; int alloc_details = scores_log_level+1; const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); 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->allocate(rsc->parent, data_set); } if(is_not_set(rsc->flags, pe_rsc_provisional)) { return rsc->allocated_to; } if(is_set(rsc->flags, pe_rsc_allocating)) { crm_debug("Dependency loop detected involving %s", rsc->id); return NULL; } set_bit(rsc->flags, pe_rsc_allocating); print_resource(alloc_details, "Allocating: ", rsc, FALSE); dump_node_scores(alloc_details, rsc, "Pre-allloc", rsc->allowed_nodes); for(gIter = rsc->rsc_cons; gIter != NULL; gIter = gIter->next) { rsc_colocation_t *constraint = (rsc_colocation_t*)gIter->data; GHashTable *archive = NULL; resource_t *rsc_rh = constraint->rsc_rh; crm_debug_2("%s: Pre-Processing %s (%s, %d, %s)", rsc->id, constraint->id, rsc_rh->id, constraint->score, role2text(constraint->role_lh)); if(constraint->role_lh >= RSC_ROLE_MASTER || (constraint->score < 0 && constraint->score > -INFINITY)) { archive = node_hash_dup(rsc->allowed_nodes); } rsc_rh->cmds->allocate(rsc_rh, data_set); rsc->cmds->rsc_colocation_lh(rsc, rsc_rh, constraint); if(archive && can_run_any(rsc->allowed_nodes) == FALSE) { crm_info("%s: Rolling back scores from %s", rsc->id, rsc_rh->id); g_hash_table_destroy(rsc->allowed_nodes); rsc->allowed_nodes = archive; archive = NULL; } if(archive) { g_hash_table_destroy(archive); } } dump_node_scores(alloc_details, rsc, "Post-coloc", rsc->allowed_nodes); for(gIter = rsc->rsc_cons_lhs; gIter != NULL; gIter = gIter->next) { rsc_colocation_t *constraint = (rsc_colocation_t*)gIter->data; rsc->allowed_nodes = constraint->rsc_lh->cmds->merge_weights( constraint->rsc_lh, rsc->id, rsc->allowed_nodes, constraint->node_attribute, constraint->score/INFINITY, TRUE, FALSE); } print_resource(LOG_DEBUG_2, "Allocating: ", rsc, FALSE); if(rsc->next_role == RSC_ROLE_STOPPED) { crm_debug_2("Making sure %s doesn't get allocated", rsc->id); /* make sure it doesnt come up again */ resource_location( rsc, NULL, -INFINITY, XML_RSC_ATTR_TARGET_ROLE, data_set); } dump_node_scores(show_scores?0:scores_log_level, rsc, __PRETTY_FUNCTION__, rsc->allowed_nodes); if(is_set(data_set->flags, pe_flag_stonith_enabled) && is_set(data_set->flags, pe_flag_have_stonith_resource) == FALSE) { clear_bit(rsc->flags, pe_rsc_managed); } if(is_not_set(rsc->flags, pe_rsc_managed)) { const char *reason = NULL; node_t *assign_to = NULL; if(rsc->running_on == NULL) { reason = "inactive"; } else if(rsc->role == RSC_ROLE_MASTER) { assign_to = rsc->running_on->data; reason = "master"; } else if(is_set(rsc->flags, pe_rsc_failed)) { reason = "failed"; } else { assign_to = rsc->running_on->data; reason = "active"; } crm_info("Unmanaged resource %s allocated to %s: %s", rsc->id, assign_to?assign_to->details->uname:"'nowhere'", reason); native_assign_node(rsc, NULL, assign_to, TRUE); } else if(is_set(data_set->flags, pe_flag_stop_everything) && safe_str_neq(class, "stonith")) { crm_debug("Forcing %s to stop", rsc->id); native_assign_node(rsc, NULL, NULL, TRUE); } else if(is_set(rsc->flags, pe_rsc_provisional) && native_choose_node(rsc, data_set) ) { crm_debug_3("Allocated resource %s to %s", rsc->id, rsc->allocated_to->details->uname); } else if(rsc->allocated_to == NULL) { if(is_not_set(rsc->flags, pe_rsc_orphan)) { crm_info("Resource %s cannot run anywhere", rsc->id); } else if(rsc->running_on != NULL) { crm_info("Stopping orphan resource %s", rsc->id); } } else { crm_debug("Pre-Allocated resource %s to %s", rsc->id, rsc->allocated_to->details->uname); } clear_bit(rsc->flags, pe_rsc_allocating); print_resource(LOG_DEBUG_3, "Allocated ", rsc, TRUE); return rsc->allocated_to; } static gboolean is_op_dup( resource_t *rsc, const char *name, const char *interval) { gboolean dup = FALSE; const char *id = NULL; const char *value = NULL; - xml_child_iter_filter( - rsc->ops_xml, operation, "op", - value = crm_element_value(operation, "name"); - if(safe_str_neq(value, name)) { - continue; - } + xmlNode *operation = NULL; + for(operation = rsc->ops_xml; operation != NULL; operation = operation->next) { + if(crm_str_eq((const char *)operation->name, "op", TRUE)) { + 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"; - } + value = crm_element_value(operation, XML_LRM_ATTR_INTERVAL); + if(value == NULL) { + value = "0"; + } - if(safe_str_neq(value, interval)) { - continue; - } + if(safe_str_neq(value, interval)) { + continue; + } - if(id == NULL) { - id = ID(operation); + 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; + } else { + crm_config_err("Operation %s is a duplicate of %s", ID(operation), id); + crm_config_err("Do not use the same (name, interval) combination more than once per resource"); + dup = TRUE; + } } - ); + } return dup; } void RecurringOp(resource_t *rsc, action_t *start, node_t *node, xmlNode *operation, pe_working_set_t *data_set) { char *key = NULL; const char *name = NULL; const char *value = NULL; const char *interval = NULL; const char *node_uname = NULL; unsigned long long interval_ms = 0; action_t *mon = NULL; gboolean is_optional = TRUE; GListPtr possible_matches = NULL; crm_debug_2("Creating recurring action %s for %s in role %s", ID(operation), rsc->id, role2text(rsc->next_role)); if(node != NULL) { node_uname = node->details->uname; } interval = crm_element_value(operation, XML_LRM_ATTR_INTERVAL); interval_ms = crm_get_interval(interval); if(interval_ms == 0) { return; } name = crm_element_value(operation, "name"); if(is_op_dup(rsc, name, interval)) { return; } if(safe_str_eq(name, RSC_STOP) || safe_str_eq(name, RSC_START) || safe_str_eq(name, RSC_DEMOTE) || safe_str_eq(name, RSC_PROMOTE) ) { crm_config_err("Invalid recurring action %s wth name: '%s'", ID(operation), name); return; } key = generate_op_key(rsc->id, name, interval_ms); if(find_rsc_op_entry(rsc, key) == NULL) { /* disabled */ crm_free(key); return; } if(start != NULL) { crm_debug_3("Marking %s %s due to %s", key, is_set(start->flags, pe_action_optional)?"optional":"manditory", start->uuid); is_optional = (get_action_flags(start, NULL) & pe_action_optional); } else { crm_debug_2("Marking %s optional", key); is_optional = TRUE; } /* start a monitor for an already active resource */ possible_matches = find_actions_exact(rsc->actions, key, node); if(possible_matches == NULL) { is_optional = FALSE; crm_debug_3("Marking %s manditory: not active", key); } else { g_list_free(possible_matches); } value = crm_element_value(operation, "role"); if((rsc->next_role == RSC_ROLE_MASTER && value == NULL) || (value != NULL && text2role(value) != rsc->next_role)) { int log_level = LOG_DEBUG_2; const char *result = "Ignoring"; if(is_optional) { char *local_key = crm_strdup(key); log_level = LOG_INFO; result = "Cancelling"; /* its running : cancel it */ mon = custom_action( rsc, local_key, RSC_CANCEL, node, FALSE, TRUE, data_set); crm_free(mon->task); mon->task = crm_strdup(RSC_CANCEL); add_hash_param(mon->meta, XML_LRM_ATTR_INTERVAL, interval); add_hash_param(mon->meta, XML_LRM_ATTR_TASK, name); local_key = NULL; switch(rsc->role) { case RSC_ROLE_SLAVE: case RSC_ROLE_STARTED: if(rsc->next_role == RSC_ROLE_MASTER) { local_key = promote_key(rsc); } else if(rsc->next_role == RSC_ROLE_STOPPED) { local_key = stop_key(rsc); } break; case RSC_ROLE_MASTER: local_key = demote_key(rsc); break; default: break; } if(local_key) { custom_action_order(rsc, NULL, mon, rsc, local_key, NULL, pe_order_runnable_left, data_set); } mon = NULL; } do_crm_log(log_level, "%s action %s (%s vs. %s)", result , key, value?value:role2text(RSC_ROLE_SLAVE), role2text(rsc->next_role)); crm_free(key); key = NULL; return; } mon = custom_action(rsc, key, name, node, is_optional, TRUE, data_set); key = mon->uuid; if(is_optional) { crm_debug_2("%s\t %s (optional)", crm_str(node_uname), mon->uuid); } if(start == NULL || is_set(start->flags, pe_action_runnable) == FALSE) { crm_debug("%s\t %s (cancelled : start un-runnable)", crm_str(node_uname), mon->uuid); update_action_flags(mon, pe_action_runnable|pe_action_clear); } 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); update_action_flags(mon, pe_action_runnable|pe_action_clear); } else if(is_set(mon->flags, pe_action_optional) == FALSE) { crm_notice(" Start recurring %s (%llus) for %s on %s", mon->task, interval_ms/1000, rsc->id, crm_str(node_uname)); } if(rsc->next_role == RSC_ROLE_MASTER) { char *running_master = crm_itoa(EXECRA_RUNNING_MASTER); add_hash_param(mon->meta, XML_ATTR_TE_TARGET_RC, running_master); crm_free(running_master); } if(node == NULL || is_set(rsc->flags, pe_rsc_managed)) { custom_action_order(rsc, start_key(rsc), NULL, NULL, crm_strdup(key), mon, pe_order_implies_then|pe_order_runnable_left, data_set); if(rsc->next_role == RSC_ROLE_MASTER) { custom_action_order( rsc, promote_key(rsc), NULL, rsc, NULL, mon, pe_order_optional|pe_order_runnable_left, data_set); } else if(rsc->role == RSC_ROLE_MASTER) { custom_action_order( rsc, demote_key(rsc), NULL, rsc, NULL, mon, pe_order_optional|pe_order_runnable_left, data_set); } } } void Recurring(resource_t *rsc, action_t *start, node_t *node, pe_working_set_t *data_set) { if(is_not_set(data_set->flags, pe_flag_maintenance_mode)) { - xml_child_iter_filter( - rsc->ops_xml, operation, "op", - RecurringOp(rsc, start, node, operation, data_set); - ); + xmlNode *operation = NULL; + for(operation = rsc->ops_xml; operation != NULL; operation = operation->next) { + if(crm_str_eq((const char *)operation->name, "op", TRUE)) { + 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; GListPtr gIter = NULL; enum rsc_role_e role = RSC_ROLE_UNKNOWN; enum rsc_role_e next_role = RSC_ROLE_UNKNOWN; crm_debug_2("Createing actions for %s: %s->%s", rsc->id, role2text(rsc->role), role2text(rsc->next_role)); chosen = rsc->allocated_to; if(chosen != NULL && rsc->next_role == RSC_ROLE_UNKNOWN) { rsc->next_role = RSC_ROLE_STARTED; } else if(rsc->next_role == RSC_ROLE_UNKNOWN) { rsc->next_role = RSC_ROLE_STOPPED; } get_rsc_attributes(rsc->parameters, rsc, chosen, data_set); for(gIter = rsc->dangling_migrations; gIter != NULL; gIter = gIter->next) { node_t *current = (node_t*)gIter->data; action_t *stop = stop_action(rsc, current, FALSE); set_bit_inplace(stop->flags, pe_action_dangle); crm_trace("Forcing a cleanup of %s on %s", rsc->id, current->details->uname); if(is_set(data_set->flags, pe_flag_remove_after_stop)) { DeleteRsc(rsc, current, FALSE, data_set); } } if(g_list_length(rsc->running_on) > 1) { if(rsc->recovery_type == recovery_stop_start) { pe_proc_warn("Attempting recovery of resource %s", rsc->id); if(rsc->role == RSC_ROLE_MASTER) { DemoteRsc(rsc, NULL, FALSE, data_set); } StopRsc(rsc, NULL, FALSE, data_set); rsc->role = RSC_ROLE_STOPPED; } } else if(rsc->running_on != NULL) { node_t *current = rsc->running_on->data; NoRoleChange(rsc, current, chosen, data_set); } else if(rsc->role == RSC_ROLE_STOPPED && rsc->next_role == RSC_ROLE_STOPPED) { char *key = start_key(rsc); GListPtr possible_matches = find_actions(rsc->actions, key, NULL); GListPtr gIter = NULL; for(gIter = possible_matches; gIter != NULL; gIter = gIter->next) { action_t *action = (action_t*)gIter->data; update_action_flags(action, pe_action_optional); /* action->pseudo = TRUE; */ } g_list_free(possible_matches); crm_debug_2("Stopping a stopped resource"); crm_free(key); goto do_recurring; } else if(rsc->role != RSC_ROLE_STOPPED) { /* A cheap trick to account for the fact that Master/Slave groups may not be * completely running when we set their role to Slave */ crm_debug_2("Resetting %s.role = %s (was %s)", rsc->id, role2text(RSC_ROLE_STOPPED), role2text(rsc->role)); rsc->role = RSC_ROLE_STOPPED; } role = rsc->role; while(role != rsc->next_role) { next_role = rsc_state_matrix[role][rsc->next_role]; crm_debug_2("Executing: %s->%s (%s)", role2text(role), role2text(next_role), rsc->id); if(rsc_action_matrix[role][next_role]( rsc, chosen, FALSE, data_set) == FALSE) { break; } role = next_role; } do_recurring: if(rsc->next_role != RSC_ROLE_STOPPED || is_set(rsc->flags, pe_rsc_managed) == FALSE) { start = start_action(rsc, chosen, TRUE); Recurring(rsc, start, chosen, data_set); } } void native_internal_constraints(resource_t *rsc, pe_working_set_t *data_set) { /* This function is on the critical path and worth optimizing as much as possible */ resource_t *top = uber_parent(rsc); int type = pe_order_optional|pe_order_implies_then|pe_order_restart; const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); custom_action_order(rsc, generate_op_key(rsc->id, RSC_STOP, 0), NULL, rsc, generate_op_key(rsc->id, RSC_START, 0), NULL, type, data_set); if(top->variant == pe_master) { custom_action_order(rsc, generate_op_key(rsc->id, RSC_DEMOTE, 0), NULL, rsc, generate_op_key(rsc->id, RSC_STOP, 0), NULL, pe_order_optional, data_set); custom_action_order(rsc, generate_op_key(rsc->id, RSC_START, 0), NULL, rsc, generate_op_key(rsc->id, RSC_PROMOTE, 0), NULL, pe_order_runnable_left, 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(safe_str_neq(class, "stonith")) { action_t *all_stopped = get_pseudo_op(ALL_STOPPED, data_set); custom_action_order( rsc, stop_key(rsc), NULL, NULL, crm_strdup(all_stopped->task), all_stopped, pe_order_implies_then|pe_order_runnable_left, data_set); } if (g_hash_table_size(rsc->utilization) > 0 && safe_str_neq(data_set->placement_strategy, "default")) { GHashTableIter iter; node_t *next = NULL; GListPtr gIter = NULL; crm_trace("Creating utilization constraints for %s - strategy: %s", rsc->id, data_set->placement_strategy); for(gIter = rsc->running_on; gIter != NULL; gIter = gIter->next) { node_t *current = (node_t*)gIter->data; char *load_stopped_task = crm_concat(LOAD_STOPPED, current->details->uname, '_'); action_t *load_stopped = get_pseudo_op(load_stopped_task, data_set); if(load_stopped->node == NULL) { load_stopped->node = node_copy(current); update_action_flags(load_stopped, pe_action_optional|pe_action_clear); } custom_action_order( rsc, stop_key(rsc), NULL, NULL, load_stopped_task, load_stopped, pe_order_optional, data_set); } g_hash_table_iter_init (&iter, rsc->allowed_nodes); while (g_hash_table_iter_next (&iter, NULL, (void**)&next)) { char *load_stopped_task = crm_concat(LOAD_STOPPED, next->details->uname, '_'); action_t *load_stopped = get_pseudo_op(load_stopped_task, data_set); if(load_stopped->node == NULL) { load_stopped->node = node_copy(next); update_action_flags(load_stopped, pe_action_optional|pe_action_clear); } custom_action_order( NULL, load_stopped_task, load_stopped, rsc, start_key(rsc), NULL, pe_order_optional, data_set); } } } void native_rsc_colocation_lh( resource_t *rsc_lh, resource_t *rsc_rh, rsc_colocation_t *constraint) { if(rsc_lh == NULL) { pe_err("rsc_lh was NULL for %s", constraint->id); return; } else if(constraint->rsc_rh == NULL) { pe_err("rsc_rh was NULL for %s", constraint->id); return; } crm_debug_2("Processing colocation constraint between %s and %s", rsc_lh->id, rsc_rh->id); rsc_rh->cmds->rsc_colocation_rh(rsc_lh, rsc_rh, constraint); } static gboolean filter_colocation_constraint( resource_t *rsc_lh, resource_t *rsc_rh, rsc_colocation_t *constraint) { int level = LOG_DEBUG_4; if(constraint->score == 0){ return FALSE; } if(constraint->score > 0 && constraint->role_lh != RSC_ROLE_UNKNOWN && constraint->role_lh != rsc_lh->next_role) { do_crm_log_unlikely(level, "LH: Skipping constraint: \"%s\" state filter", role2text(constraint->role_rh)); return FALSE; } if(constraint->score > 0 && constraint->role_rh != RSC_ROLE_UNKNOWN && constraint->role_rh != rsc_rh->next_role) { do_crm_log_unlikely(level, "RH: Skipping constraint: \"%s\" state filter", role2text(constraint->role_rh)); return FALSE; } if(constraint->score < 0 && constraint->role_lh != RSC_ROLE_UNKNOWN && constraint->role_lh == rsc_lh->next_role) { do_crm_log_unlikely(level, "LH: Skipping -ve constraint: \"%s\" state filter", role2text(constraint->role_rh)); return FALSE; } if(constraint->score < 0 && constraint->role_rh != RSC_ROLE_UNKNOWN && constraint->role_rh == rsc_rh->next_role) { do_crm_log_unlikely(level, "RH: Skipping -ve constraint: \"%s\" state filter", role2text(constraint->role_rh)); return FALSE; } return TRUE; } static void colocation_match( resource_t *rsc_lh, resource_t *rsc_rh, rsc_colocation_t *constraint) { const char *tmp = NULL; const char *value = NULL; const char *attribute = "#id"; GHashTable *work = NULL; gboolean do_check = FALSE; GHashTableIter iter; node_t *node = NULL; 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; } work = node_hash_dup(rsc_lh->allowed_nodes); g_hash_table_iter_init (&iter, work); while (g_hash_table_iter_next (&iter, NULL, (void**)&node)) { tmp = g_hash_table_lookup(node->details->attrs, attribute); if(do_check && safe_str_eq(tmp, value)) { if(constraint->score < INFINITY) { crm_debug_2("%s: %s.%s += %d", constraint->id, rsc_lh->id, node->details->uname, constraint->score); node->weight = merge_weights( constraint->score, node->weight); } } else if(do_check == FALSE || constraint->score >= INFINITY) { crm_debug_2("%s: %s.%s -= %d (%s)", constraint->id, rsc_lh->id, node->details->uname, constraint->score, do_check?"failed":"unallocated"); node->weight = merge_weights(-constraint->score, node->weight); } } if(can_run_any(work) || constraint->score <= -INFINITY || constraint->score >= INFINITY) { g_hash_table_destroy(rsc_lh->allowed_nodes); rsc_lh->allowed_nodes = work; work = NULL; } else { char *score = score2char(constraint->score); crm_info("%s: Rolling back scores from %s (%d, %s)", rsc_lh->id, rsc_rh->id, do_check, score); crm_free(score); } if(work) { g_hash_table_destroy(work); } } 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); } } const char *convert_non_atomic_task(char *raw_task, resource_t *rsc, gboolean allow_notify); const char * convert_non_atomic_task(char *raw_task, resource_t *rsc, gboolean allow_notify) { int task = no_action; const char *atomic_task = raw_task; crm_trace("Processing %s for %s", crm_str(raw_task), rsc->id); if(raw_task == NULL) { return NULL; } else if(strstr(raw_task, "notify") != NULL) { goto done; /* no conversion */ } else if(rsc->variant < pe_group) { goto done; /* no conversion */ } task = text2task(raw_task); switch(task) { case stop_rsc: case start_rsc: case action_notify: case action_promote: case action_demote: break; case stopped_rsc: case started_rsc: case action_notified: case action_promoted: case action_demoted: task--; break; case monitor_rsc: case shutdown_crm: case stonith_node: goto done; break; default: crm_trace("Unknown action: %s", raw_task); goto done; break; } if(task != no_action) { if(is_set(rsc->flags, pe_rsc_notify) && allow_notify) { /* atomic_task = generate_notify_key(rid, "confirmed-post", task2text(task+1)); */ crm_err("Not handled"); } else { atomic_task = task2text(task+1); } crm_trace("Converted %s -> %s", raw_task, atomic_task); } done: return atomic_task; } enum pe_action_flags native_action_flags(action_t *action, node_t *node) { return action->flags; } enum pe_graph_flags native_update_actions( action_t *first, action_t *then, node_t *node, enum pe_action_flags flags, enum pe_action_flags filter, enum pe_ordering type) { enum pe_graph_flags changed = pe_graph_none; enum pe_action_flags then_flags = then->flags; enum pe_action_flags first_flags = first->flags; if(type & pe_order_implies_first) { if((filter & pe_action_optional) && (flags & pe_action_optional) == 0) { clear_bit_inplace(first->flags, pe_action_optional); } } if(is_set(type, pe_order_runnable_left) && is_set(filter, pe_action_runnable) && is_set(flags, pe_action_runnable) == FALSE) { clear_bit_inplace(then->flags, pe_action_runnable); } if(is_set(type, pe_order_implies_then) && is_set(filter, pe_action_optional) && is_set(flags, pe_action_optional) == FALSE) { clear_bit_inplace(then->flags, pe_action_optional); } if(is_set(type, pe_order_restart)) { gboolean unset = FALSE; crm_trace("Handle restart 0x%.6x 0x%.6x", filter, then->flags); CRM_ASSERT(then->rsc == first->rsc); CRM_ASSERT(then->rsc->variant == pe_native); if((filter & pe_action_runnable) && (then->flags & pe_action_runnable) == 0) { crm_trace("Handling shutdown: %s -> %s %d", first->uuid, then->uuid, is_set(first->flags, pe_action_runnable)); unset = TRUE; } if((filter & pe_action_optional) && (then->flags & pe_action_optional) == 0) { crm_trace("Handling recover: %s -> %s %d", first->uuid, then->uuid, is_set(first->flags, pe_action_runnable)); unset = TRUE; } if(unset && is_set(first->flags, pe_action_runnable)) { clear_bit_inplace(first->flags, pe_action_optional); } } if(then_flags != then->flags) { changed |= pe_graph_updated_then; crm_trace("Flags for %s on %s are now 0x%.6x (were 0x%.6x)", then->uuid, then->node?then->node->details->uname:"[none]", then->flags, then_flags); } if(first_flags != first->flags) { changed |= pe_graph_updated_first; crm_trace("Flags for %s on %s are now 0x%.6x (were 0x%.6x)", first->uuid, first->node?first->node->details->uname:"[none]", first->flags, first_flags); } return changed; } void native_rsc_location(resource_t *rsc, rsc_to_node_t *constraint) { GListPtr gIter = NULL; GHashTableIter iter; node_t *node = NULL; 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; } for(gIter = constraint->node_list_rh; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t*)gIter->data; node_t *other_node = NULL; other_node = (node_t*)pe_hash_table_lookup( rsc->allowed_nodes, node->details->id); if(other_node != NULL) { crm_debug_4("%s + %s: %d + %d", node->details->uname, other_node->details->uname, node->weight, other_node->weight); other_node->weight = merge_weights( other_node->weight, node->weight); } else { node_t *new_node = node_copy(node); g_hash_table_insert(rsc->allowed_nodes, (gpointer)new_node->details->id, new_node); } } g_hash_table_iter_init (&iter, rsc->allowed_nodes); while (g_hash_table_iter_next (&iter, NULL, (void**)&node)) { 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) { GListPtr gIter = NULL; crm_debug_3("Processing actions from %s", rsc->id); for(gIter = rsc->actions; gIter != NULL; gIter = gIter->next) { action_t *action = (action_t*)gIter->data; crm_debug_4("processing action %d for rsc=%s", action->id, rsc->id); graph_element_from_action(action, data_set); } for(gIter = rsc->children; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t*)gIter->data; child_rsc->cmds->expand(child_rsc, data_set); } } void LogActions(resource_t *rsc, pe_working_set_t *data_set) { node_t *next = NULL; node_t *current = NULL; gboolean moving = FALSE; if(rsc->children) { GListPtr gIter = NULL; for(gIter = rsc->children; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t*)gIter->data; LogActions(child_rsc, data_set); } return; } next = rsc->allocated_to; if(rsc->running_on) { current = rsc->running_on->data; if(rsc->role == RSC_ROLE_STOPPED) { /* * This can occur when resources are being recovered * We fiddle with the current role in native_create_actions() */ rsc->role = RSC_ROLE_STARTED; } } if(current == NULL && is_set(rsc->flags, pe_rsc_orphan)) { /* Don't log stopped orphans */ return; } if(is_not_set(rsc->flags, pe_rsc_managed) || (current == NULL && next == NULL)) { crm_notice("Leave resource %s\t(%s%s)", rsc->id, role2text(rsc->role), is_not_set(rsc->flags, pe_rsc_managed)?" unmanaged":""); return; } if(current != NULL && next != NULL && safe_str_neq(current->details->id, next->details->id)) { moving = TRUE; } if(rsc->role == rsc->next_role) { action_t *start = NULL; char *key = start_key(rsc); GListPtr possible_matches = find_actions(rsc->actions, key, next); crm_free(key); if(possible_matches) { start = possible_matches->data; g_list_free(possible_matches); } key = generate_op_key(rsc->id, CRMD_ACTION_MIGRATED, 0); possible_matches = find_actions(rsc->actions, key, next); crm_free(key); CRM_CHECK(next != NULL,); if(next == NULL) { } else if(possible_matches && current) { crm_notice("Migrate resource %s\t(%s %s -> %s)", rsc->id, role2text(rsc->role), current->details->uname, next->details->uname); g_list_free(possible_matches); } else if(start == NULL || is_set(start->flags, pe_action_optional)) { crm_notice("Leave resource %s\t(%s %s)", rsc->id, role2text(rsc->role), next->details->uname); } else if(moving && current) { crm_notice("Move resource %s\t(%s %s -> %s)", rsc->id, role2text(rsc->role), current->details->uname, next->details->uname); } else if(is_set(rsc->flags, pe_rsc_failed)) { crm_notice("Recover resource %s\t(%s %s)", rsc->id, role2text(rsc->role), next->details->uname); } else if(start && is_set(start->flags, pe_action_runnable) == FALSE) { crm_notice("Stop resource %s\t(%s %s)", rsc->id, role2text(rsc->role), next->details->uname); } else { crm_notice("Restart resource %s\t(%s %s)", rsc->id, role2text(rsc->role), next->details->uname); } return; } if(rsc->role > RSC_ROLE_SLAVE && rsc->role > rsc->next_role) { CRM_CHECK(current != NULL,); if(current != NULL) { crm_notice("Demote %s\t(%s -> %s %s)", rsc->id, role2text(rsc->role), role2text(rsc->next_role), current->details->uname); } } if(rsc->next_role == RSC_ROLE_STOPPED || moving) { GListPtr gIter = NULL; CRM_CHECK(current != NULL,); for(gIter = rsc->running_on; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t*)gIter->data; crm_notice("Stop resource %s\t(%s)", rsc->id, node->details->uname); } } if(rsc->role == RSC_ROLE_STOPPED || moving) { CRM_CHECK(next != NULL,); if(next != NULL) { crm_notice("Start %s\t(%s)", rsc->id, next->details->uname); } } if(rsc->next_role > RSC_ROLE_SLAVE && rsc->role < rsc->next_role) { CRM_CHECK(next != NULL,); crm_notice("Promote %s\t(%s -> %s %s)", rsc->id, role2text(rsc->role), role2text(rsc->next_role), next->details->uname); } } void NoRoleChange(resource_t *rsc, node_t *current, node_t *next, pe_working_set_t *data_set) { GListPtr gIter = NULL; action_t *stop = NULL; action_t *start = NULL; GListPtr possible_matches = NULL; crm_debug_2("Executing: %s (role=%s)", rsc->id, role2text(rsc->next_role)); if(current == NULL || next == NULL) { return; } if(is_set(rsc->flags, pe_rsc_failed) || safe_str_neq(current->details->id, next->details->id)) { if(rsc->next_role > RSC_ROLE_STARTED) { gboolean optional = TRUE; if(rsc->role == RSC_ROLE_MASTER) { optional = FALSE; } DemoteRsc(rsc, current, optional, data_set); } if(rsc->role == RSC_ROLE_MASTER) { DemoteRsc(rsc, current, FALSE, data_set); } StopRsc(rsc, current, FALSE, data_set); StartRsc(rsc, next, FALSE, data_set); if(rsc->next_role == RSC_ROLE_MASTER) { PromoteRsc(rsc, next, FALSE, data_set); } possible_matches = find_recurring_actions(rsc->actions, next); for(gIter = possible_matches; gIter != NULL; gIter = gIter->next) { action_t *match = (action_t*)gIter->data; if(is_set(match->flags, pe_action_optional) == FALSE) { crm_debug("Fixing recurring action: %s", match->uuid); update_action_flags(match, pe_action_optional); } } g_list_free(possible_matches); } else if(is_set(rsc->flags, pe_rsc_start_pending)) { start = start_action(rsc, next, TRUE); if(is_set(start->flags, pe_action_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); if(is_set(start->flags, pe_action_optional)) { update_action_flags(stop, pe_action_optional); } else { update_action_flags(stop, pe_action_optional|pe_action_clear); } if(rsc->next_role > RSC_ROLE_STARTED) { DemoteRsc(rsc, current, is_set(start->flags, pe_action_optional), data_set); } StopRsc(rsc, current, is_set(start->flags, pe_action_optional), data_set); StartRsc(rsc, current, is_set(start->flags, pe_action_optional), data_set); if(rsc->next_role == RSC_ROLE_MASTER) { PromoteRsc(rsc, next, is_set(start->flags, pe_action_optional), data_set); } if(is_set(start->flags, pe_action_runnable) == FALSE) { rsc->next_role = RSC_ROLE_STOPPED; } } } gboolean StopRsc(resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set) { GListPtr gIter = 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_optional|pe_order_stonith_stop, data_set); } for(gIter = rsc->running_on; gIter != NULL; gIter = gIter->next) { node_t *current = (node_t*)gIter->data; stop_action(rsc, current, optional); if(is_set(data_set->flags, pe_flag_remove_after_stop)) { DeleteRsc(rsc, current, optional, data_set); } } return TRUE; } gboolean StartRsc(resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set) { action_t *start = NULL; crm_debug_2("Executing: %s", rsc->id); start = start_action(rsc, next, TRUE); if(is_set(start->flags, pe_action_runnable) && optional == FALSE) { update_action_flags(start, pe_action_optional|pe_action_clear); } return TRUE; } gboolean PromoteRsc(resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set) { char *key = NULL; GListPtr gIter = 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); for(gIter = action_list; gIter != NULL; gIter = gIter->next) { action_t *start = (action_t*)gIter->data; if(is_set(start->flags, pe_action_runnable) == FALSE) { runnable = FALSE; } } g_list_free(action_list); if(runnable) { promote_action(rsc, next, optional); return TRUE; } crm_debug("%s\tPromote %s (canceled)", next->details->uname, rsc->id); key = promote_key(rsc); action_list = find_actions_exact(rsc->actions, key, next); crm_free(key); for(gIter = action_list; gIter != NULL; gIter = gIter->next) { action_t *promote = (action_t*)gIter->data; update_action_flags(promote, pe_action_runnable|pe_action_clear); } g_list_free(action_list); return TRUE; } gboolean DemoteRsc(resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set) { GListPtr gIter = NULL; crm_debug_2("Executing: %s", rsc->id); /* CRM_CHECK(rsc->next_role == RSC_ROLE_SLAVE, return FALSE); */ for(gIter = rsc->running_on; gIter != NULL; gIter = gIter->next) { node_t *current = (node_t*)gIter->data; demote_action(rsc, current, optional); } return TRUE; } gboolean RoleError(resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set) { crm_debug("Executing: %s", rsc->id); CRM_CHECK(FALSE, return FALSE); return FALSE; } gboolean NullOp(resource_t *rsc, node_t *next, gboolean optional, pe_working_set_t *data_set) { crm_debug_2("Executing: %s", rsc->id); return FALSE; } gboolean DeleteRsc(resource_t *rsc, node_t *node, gboolean optional, pe_working_set_t *data_set) { action_t *delete = NULL; #if DELETE_THEN_REFRESH action_t *refresh = NULL; #endif if(is_set(rsc->flags, pe_rsc_failed)) { crm_debug_2("Resource %s not deleted from %s: failed", rsc->id, node->details->uname); return FALSE; } else if(node == NULL) { crm_debug_2("Resource %s not deleted: NULL node", rsc->id); return FALSE; } else if(node->details->unclean || node->details->online == FALSE) { crm_debug_2("Resource %s not deleted from %s: unrunnable", rsc->id, node->details->uname); return FALSE; } crm_notice("Removing %s from %s", rsc->id, node->details->uname); delete = delete_action(rsc, node, optional); new_rsc_order(rsc, RSC_STOP, rsc, RSC_DELETE, optional?pe_order_implies_then:pe_order_optional, data_set); new_rsc_order(rsc, RSC_DELETE, rsc, RSC_START, optional?pe_order_implies_then:pe_order_optional, data_set); #if DELETE_THEN_REFRESH refresh = custom_action( NULL, crm_strdup(CRM_OP_LRM_REFRESH), CRM_OP_LRM_REFRESH, node, FALSE, TRUE, data_set); add_hash_param(refresh->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE); order_actions(delete, refresh, pe_order_optional); #endif return TRUE; } #include <../lib/pengine/unpack.h> static node_t * probe_grouped_clone(resource_t *rsc, node_t *node, pe_working_set_t *data_set) { node_t *running = NULL; resource_t *top = uber_parent(rsc); if(running == NULL && is_set(top->flags, pe_rsc_unique) == FALSE) { /* Annoyingly we also need to check any other clone instances * Clumsy, but it will work. * * An alternative would be to update known_on for every peer * during process_rsc_state() * * This code desperately needs optimization * ptest -x with 100 nodes, 100 clones and clone-max=10: * No probes O(25s) * Detection without clone loop O(3m) * Detection with clone loop O(8m) ptest[32211]: 2010/02/18_14:27:55 CRIT: stage5: Probing for unknown resources ptest[32211]: 2010/02/18_14:33:39 CRIT: stage5: Done ptest[32211]: 2010/02/18_14:35:05 CRIT: stage7: Updating action states ptest[32211]: 2010/02/18_14:35:05 CRIT: stage7: Done */ char *clone_id = clone_zero(rsc->id); resource_t *peer = pe_find_resource(top->children, clone_id); while(peer && running == NULL) { running = pe_hash_table_lookup(peer->known_on, node->details->id); if(running != NULL) { /* we already know the status of the resource on this node */ crm_debug_3("Skipping active clone: %s", rsc->id); crm_free(clone_id); return running; } clone_id = increment_clone(clone_id); peer = pe_find_resource(data_set->resources, clone_id); } crm_free(clone_id); } return running; } gboolean native_create_probe(resource_t *rsc, node_t *node, action_t *complete, gboolean force, pe_working_set_t *data_set) { char *key = NULL; action_t *probe = NULL; node_t *running = NULL; resource_t *top = uber_parent(rsc); static const char *rc_master = NULL; static const char *rc_inactive = NULL; if(rc_inactive == NULL) { rc_inactive = crm_itoa(EXECRA_NOT_RUNNING); rc_master = crm_itoa(EXECRA_RUNNING_MASTER); } CRM_CHECK(node != NULL, return FALSE); if(force == FALSE && is_not_set(data_set->flags, pe_flag_startup_probes)) { crm_debug_2("Skipping active resource detection for %s", rsc->id); return FALSE; } if(rsc->children) { GListPtr gIter = NULL; gboolean any_created = FALSE; for(gIter = rsc->children; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t*)gIter->data; 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 = g_hash_table_lookup(rsc->known_on, node->details->id); if(running == NULL && is_set(rsc->flags, pe_rsc_unique) == FALSE) { /* Anonymous clones */ if(rsc->parent == top) { running = g_hash_table_lookup(rsc->parent->known_on, node->details->id); } else { /* Grouped anonymous clones need extra special handling */ running = probe_grouped_clone(rsc, node, data_set); } } 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, RSC_STATUS, 0); probe = custom_action(rsc, key, RSC_STATUS, node, FALSE, TRUE, data_set); update_action_flags(probe, pe_action_optional|pe_action_clear); if(running == NULL) { add_hash_param(probe->meta, XML_ATTR_TE_TARGET_RC, rc_inactive); } else if(rsc->role == RSC_ROLE_MASTER) { add_hash_param(probe->meta, XML_ATTR_TE_TARGET_RC, rc_master); } crm_debug("Probing %s on %s (%s)", rsc->id, node->details->uname, role2text(rsc->role)); order_actions(probe, complete, pe_order_implies_then); return TRUE; } static void native_start_constraints( resource_t *rsc, action_t *stonith_op, gboolean is_stonith, pe_working_set_t *data_set) { node_t *target = stonith_op?stonith_op->node:NULL; if(is_stonith) { char *key = start_key(rsc); action_t *ready = get_pseudo_op(STONITH_UP, data_set); crm_debug_2("Ordering %s action before stonith events", key); custom_action_order( rsc, key, NULL, NULL, crm_strdup(ready->task), ready, pe_order_optional|pe_order_implies_then, data_set); } else { GListPtr gIter = NULL; action_t *all_stopped = get_pseudo_op(ALL_STOPPED, data_set); action_t *stonith_done = get_pseudo_op(STONITH_DONE, data_set); for(gIter = rsc->actions; gIter != NULL; gIter = gIter->next) { action_t *action = (action_t*)gIter->data; if(action->needs == rsc_req_stonith) { order_actions(stonith_done, action, pe_order_optional); } else if(target != NULL && safe_str_eq(action->task, RSC_START) && NULL == pe_hash_table_lookup(rsc->known_on, target->details->id)) { /* if known == NULL, then we dont know if * the resource is active on the node * we're about to shoot * * in this case, regardless of action->needs, * the only safe option is to wait until * the node is shot before doing anything * to with the resource * * its analogous to waiting for all the probes * for rscX to complete before starting rscX * * the most likely explaination is that the * DC died and took its status with it */ crm_debug("Ordering %s after %s recovery", action->uuid, target->details->uname); order_actions(all_stopped, action, pe_order_optional|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 gIter = NULL; GListPtr action_list = NULL; resource_t *top = uber_parent(rsc); key = stop_key(rsc); action_list = find_actions(rsc->actions, key, stonith_op->node); crm_free(key); /* add the stonith OP as a stop pre-req and the mark the stop * as a pseudo op - since its now redundant */ for(gIter = action_list; gIter != NULL; gIter = gIter->next) { action_t *action = (action_t*)gIter->data; if(action->node->details->online && action->node->details->unclean == FALSE && is_set(rsc->flags, pe_rsc_failed)) { continue; } if(is_set(rsc->flags, pe_rsc_failed)) { crm_warn("Stop of failed resource %s is" " implicit after %s is fenced", rsc->id, action->node->details->uname); } else { crm_info("%s is implicit after %s is fenced", action->uuid, action->node->details->uname); } /* the stop would never complete and is * now implied by the stonith operation */ update_action_flags(action, pe_action_pseudo); update_action_flags(action, pe_action_runnable); update_action_flags(action, pe_action_implied_by_stonith); if(is_stonith == FALSE) { action_t *parent_stop = find_first_action(top->actions, NULL, RSC_STOP, NULL); order_actions(stonith_op, action, pe_order_optional); order_actions(stonith_op, parent_stop, pe_order_optional); } if(is_set(rsc->flags, pe_rsc_notify)) { /* Create a second notification that will be delivered * immediately after the node is fenced * * Basic problem: * - C is a clone active on the node to be shot and stopping on another * - R is a resource that depends on C * * + C.stop depends on R.stop * + C.stopped depends on STONITH * + C.notify depends on C.stopped * + C.healthy depends on C.notify * + R.stop depends on C.healthy * * The extra notification here changes * + C.healthy depends on C.notify * into: * + C.healthy depends on C.notify' * + C.notify' depends on STONITH' * thus breaking the loop */ notify_data_t *n_data = create_notification_boundaries(rsc, RSC_STOP, NULL, stonith_op, data_set); crm_info("Creating secondary notification for %s", action->uuid); collect_notification_data(rsc, TRUE, FALSE, n_data); g_hash_table_insert(n_data->keys, crm_strdup("notify_stop_resource"), crm_strdup(rsc->id)); g_hash_table_insert(n_data->keys, crm_strdup("notify_stop_uname"), crm_strdup(action->node->details->uname)); create_notifications(uber_parent(rsc), n_data, data_set); free_notification_data(n_data); } /* From Bug #1601, successful fencing must be an input to a failed resources stop action. However given group(rA, rB) running on nodeX and B.stop has failed, A := stop healthy resource (rA.stop) B := stop failed resource (pseudo operation B.stop) C := stonith nodeX A requires B, B requires C, C requires A This loop would prevent the cluster from making progress. This block creates the "C requires A" dependency and therefore must (at least for now) be disabled. Instead, run the block above and treat all resources on nodeX as B would be (marked as a pseudo op depending on the STONITH). TODO: Break the "A requires B" dependency in update_action() and re-enable this block } else if(is_stonith == FALSE) { crm_info("Moving healthy resource %s" " off %s before fencing", rsc->id, node->details->uname); * stop healthy resources before the * stonith op * custom_action_order( rsc, stop_key(rsc), NULL, NULL,crm_strdup(CRM_OP_FENCE),stonith_op, pe_order_optional, data_set); */ } g_list_free(action_list); key = demote_key(rsc); action_list = find_actions(rsc->actions, key, stonith_op->node); crm_free(key); for(gIter = action_list; gIter != NULL; gIter = gIter->next) { action_t *action = (action_t*)gIter->data; if(action->node->details->online == FALSE || is_set(rsc->flags, pe_rsc_failed)) { crm_info("Demote of failed resource %s is" " implict after %s is fenced", rsc->id, action->node->details->uname); /* the stop would never complete and is * now implied by the stonith operation */ crm_trace("here - 1"); update_action_flags(action, pe_action_pseudo); update_action_flags(action, pe_action_runnable); if(is_stonith == FALSE) { order_actions(stonith_op, action, pe_order_optional); } } } g_list_free(action_list); } void rsc_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) { GListPtr gIter = NULL; for(gIter = rsc->children; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t*)gIter->data; rsc_stonith_ordering(child_rsc, stonith_op, data_set); } return; } if(is_not_set(rsc->flags, pe_rsc_managed)) { crm_debug_3("Skipping fencing constraints for unmanaged resource: %s", rsc->id); return; } if(stonith_op != NULL && safe_str_eq(class, "stonith")) { is_stonith = TRUE; } /* Start constraints */ native_start_constraints(rsc, stonith_op, is_stonith, data_set); /* Stop constraints */ native_stop_constraints(rsc, stonith_op, is_stonith, data_set); } enum stack_activity { stack_stable = 0, stack_starting = 1, stack_stopping = 2, stack_middle = 4, }; static enum stack_activity find_clone_activity_on(resource_t *rsc, resource_t *target, node_t *node, const char *type) { int mode = stack_stable; action_t *active = NULL; if(target->children) { GListPtr gIter = NULL; for(gIter = target->children; gIter != NULL; gIter = gIter->next) { resource_t *child = (resource_t*)gIter->data; mode |= find_clone_activity_on(rsc, child, node, type); } return mode; } active = find_first_action(target->actions, NULL, CRMD_ACTION_START, NULL); if(active && is_set(active->flags, pe_action_optional) == FALSE && is_set(active->flags, pe_action_pseudo) == FALSE) { crm_debug("%s: found scheduled %s action (%s)", rsc->id, active->uuid, type); mode |= stack_starting; } active = find_first_action(target->actions, NULL, CRMD_ACTION_STOP, node); if(active && is_set(active->flags, pe_action_optional) == FALSE && is_set(active->flags, pe_action_pseudo) == FALSE) { crm_debug("%s: found scheduled %s action (%s)", rsc->id, active->uuid, type); mode |= stack_stopping; } return mode; } static enum stack_activity check_stack_element(resource_t *rsc, resource_t *other_rsc, const char *type) { resource_t *other_p = uber_parent(other_rsc); if(other_rsc == NULL || other_rsc == rsc) { return stack_stable; } else if(other_p->variant == pe_native) { crm_notice("Cannot migrate %s due to dependency on %s (%s)", rsc->id, other_rsc->id, type); return stack_middle; } else if(other_rsc == rsc->parent) { int mode = 0; GListPtr gIter = NULL; for(gIter = other_rsc->rsc_cons; gIter != NULL; gIter = gIter->next) { rsc_colocation_t *constraint = (rsc_colocation_t*)gIter->data; if(constraint->score > 0) { mode |= check_stack_element(rsc, constraint->rsc_rh, type); } } return mode; } else if(other_p->variant == pe_group) { crm_notice("Cannot migrate %s due to dependency on group %s (%s)", rsc->id, other_rsc->id, type); return stack_middle; } /* else: >= clone */ /* ## Assumption A depends on clone(B) ## Resource Activity During Move N1 N2 N3 --- --- --- t0 A.stop t1 B.stop B.stop t2 B.start B.start t3 A.start ## Resource Activity During Migration N1 N2 N3 --- --- --- t0 B.start B.start t1 A.stop (1) t2 A.start (2) t3 B.stop B.stop Node 1: Rewritten to be a migrate-to operation Node 2: Rewritten to be a migrate-from operation # Constraints The following constraints already exist in the system. The 'ok' and 'fail' column refers to whether they still hold for migration. a) A.stop -> A.start - ok b) B.stop -> B.start - fail c) A.stop -> B.stop - ok d) B.start -> A.start - ok e) B.stop -> A.start - fail f) A.stop -> B.start - fail ## Scenarios B unchanged - ok B stopping only - fail - possible after fixing 'e' B starting only - fail - possible after fixing 'f' B stoping and starting - fail - constraint 'b' is unfixable B restarting only on N2 - fail - as-per previous only rarer */ /* Only allow migration when the clone is either stable, only starting or only stopping */ return find_clone_activity_on(rsc, other_rsc, NULL, type); } static gboolean at_stack_bottom(resource_t *rsc) { char *key = NULL; action_t *start = NULL; action_t *other = NULL; int mode = stack_stable; GListPtr action_list = NULL; GListPtr gIter = 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); for(gIter = rsc->rsc_cons; gIter != NULL; gIter = gIter->next) { rsc_colocation_t *constraint = (rsc_colocation_t*)gIter->data; resource_t *target = constraint->rsc_rh; crm_debug_4("Checking %s: %s == %s (%d)", constraint->id, rsc->id, target->id, constraint->score); if(constraint->score > 0) { mode |= check_stack_element(rsc, target, "coloc"); if(mode & stack_middle) { return FALSE; } else if((mode & stack_stopping) && (mode & stack_starting)) { crm_notice("Cannot migrate %s due to colocation activity (last was %s)", rsc->id, target->id); return FALSE; } } } for(gIter = start->actions_before; gIter != NULL; gIter = gIter->next) { action_wrapper_t *other_w = (action_wrapper_t*)gIter->data; other = other_w->action; if(other_w->type & pe_order_serialize_only) { crm_debug_3("%s: depends on %s (serialize ordering)", rsc->id, other->uuid); continue; } crm_debug_2("%s: Checking %s ordering", rsc->id, other->uuid); if(is_set(other->flags, pe_action_optional) == FALSE) { mode |= check_stack_element(rsc, other->rsc, "order"); if(mode & stack_middle) { return FALSE; } else if((mode & stack_stopping) && (mode & stack_starting)) { crm_notice("Cannot migrate %s due to ordering activity (last was %s)", rsc->id, other->rsc->id); return FALSE; } } } return TRUE; } void rsc_migrate_reload(resource_t *rsc, pe_working_set_t *data_set) { char *key = NULL; GListPtr gIter = 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) { for(gIter = rsc->children; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t*)gIter->data; rsc_migrate_reload(child_rsc, data_set); } other = NULL; return; } else if(rsc->variant > pe_native) { return; } do_crm_log_unlikely(level+1, "Processing %s", rsc->id); if(is_not_set(rsc->flags, pe_rsc_managed) || is_set(rsc->flags, pe_rsc_failed) || is_set(rsc->flags, pe_rsc_start_pending) || rsc->next_role < RSC_ROLE_STARTED || g_list_length(rsc->running_on) != 1) { do_crm_log_unlikely( level+1, "%s: general resource state: flags=0x%.16llx", rsc->id, rsc->flags); return; } value = g_hash_table_lookup(rsc->meta, XML_OP_ATTR_ALLOW_MIGRATE); if(crm_is_true(value)) { set_bit(rsc->flags, pe_rsc_can_migrate); } if(rsc->next_role > RSC_ROLE_SLAVE) { clear_bit(rsc->flags, pe_rsc_can_migrate); do_crm_log_unlikely( level+1, "%s: resource role: role=%s", rsc->id, role2text(rsc->next_role)); } key = start_key(rsc); action_list = find_actions(rsc->actions, key, NULL); crm_free(key); if(action_list == NULL) { do_crm_log_unlikely(level, "%s: no start action", rsc->id); return; } start = action_list->data; g_list_free(action_list); if(is_not_set(rsc->flags, pe_rsc_can_migrate) && is_set(start->flags, pe_action_allow_reload_conversion) == FALSE) { do_crm_log_unlikely(level+1, "%s: no need to continue", rsc->id); return; } key = stop_key(rsc); action_list = find_actions(rsc->actions, key, NULL); crm_free(key); if(action_list == NULL) { do_crm_log_unlikely(level, "%s: no stop action", rsc->id); return; } stop = action_list->data; g_list_free(action_list); action = start; if(action->node == NULL || is_set(action->flags, pe_action_pseudo) || is_set(action->flags, pe_action_optional) || is_set(action->flags, pe_action_runnable) == FALSE) { do_crm_log_unlikely(level, "%s: %s", rsc->id, action->task); return; } action = stop; if(action->node == NULL || is_set(action->flags, pe_action_pseudo) || is_set(action->flags, pe_action_optional) || is_set(action->flags, pe_action_runnable) == FALSE) { do_crm_log_unlikely(level, "%s: %s", rsc->id, action->task); return; } if(is_set(rsc->flags, pe_rsc_can_migrate)) { if(start->node == NULL || stop->node == NULL || stop->node->details == start->node->details) { clear_bit(rsc->flags, pe_rsc_can_migrate); } else if(at_stack_bottom(rsc) == FALSE) { clear_bit(rsc->flags, pe_rsc_can_migrate); } } if(is_set(rsc->flags, pe_rsc_can_migrate)) { action_t *to = NULL; action_t *from = NULL; action_t *done = get_pseudo_op(STONITH_DONE, data_set); crm_info("Migrating %s from %s to %s", rsc->id, stop->node->details->uname, start->node->details->uname); /* Preserve the stop to ensure the end state is sane on that node, * Make the start a pseudo op * Create migrate_to, have it depend on everything the stop did * Create migrate_from * *-> migrate_to -> migrate_from -> stop -> start */ update_action_flags(start, pe_action_pseudo); /* easier than trying to delete it from the graph * but perhaps we should have it run anyway */ to = custom_action(rsc, generate_op_key(rsc->id, RSC_MIGRATE, 0), RSC_MIGRATE, stop->node, FALSE, TRUE, data_set); from = custom_action(rsc, generate_op_key(rsc->id, RSC_MIGRATED, 0), RSC_MIGRATED, start->node, FALSE, TRUE, data_set); /* This is slightly sub-optimal if 'to' fails, but always * run both halves of the migration before terminating the * transition. * * This can be removed if/when we update unpack_rsc_op() to * 'correctly' handle partial migrations. * * Without this, we end up stopping both sides */ from->priority = INFINITY; order_actions(to, from, pe_order_optional); order_actions(from, stop, pe_order_optional); order_actions(done, to, pe_order_optional); add_hash_param(to->meta, XML_LRM_ATTR_MIGRATE_SOURCE, stop->node->details->id); add_hash_param(to->meta, XML_LRM_ATTR_MIGRATE_TARGET, start->node->details->id); add_hash_param(from->meta, XML_LRM_ATTR_MIGRATE_SOURCE, stop->node->details->id); add_hash_param(from->meta, XML_LRM_ATTR_MIGRATE_TARGET, start->node->details->id); /* Create the correct ordering ajustments based on find_clone_activity_on(); */ for(gIter = rsc->rsc_cons; gIter != NULL; gIter = gIter->next) { rsc_colocation_t *constraint = (rsc_colocation_t*)gIter->data; resource_t *target = constraint->rsc_rh; crm_info("Repairing %s: %s == %s (%d)", constraint->id, rsc->id, target->id, constraint->score); if(constraint->score > 0) { int mode = check_stack_element(rsc, target, "coloc"); action_t *clone_stop = find_first_action(target->actions, NULL, RSC_STOP, NULL); action_t *clone_start = find_first_action(target->actions, NULL, RSC_STARTED, NULL); CRM_ASSERT(clone_stop != NULL); CRM_ASSERT(clone_start != NULL); CRM_ASSERT((mode & stack_middle) == 0); CRM_ASSERT(((mode & stack_stopping) && (mode & stack_starting)) == 0); if(mode & stack_stopping) { #if 0 crm_debug("Creating %s.start -> %s.stop ordering", rsc->id, target->id); order_actions(from, clone_stop, pe_order_optional); #endif GListPtr lpc2 = NULL; for(lpc2 = start->actions_before; lpc2 != NULL; lpc2 = lpc2->next) { action_wrapper_t *other_w = (action_wrapper_t*)lpc2->data; /* Needed if the clone's started pseudo-action ever gets printed in the graph */ if(other_w->action == clone_start) { crm_debug("Breaking %s -> %s ordering", other_w->action->uuid, start->uuid); other_w->type = pe_order_none; } } } else if(mode & stack_starting) { #if 0 crm_debug("Creating %s.started -> %s.stop ordering", target->id, rsc->id); order_actions(clone_start, to, pe_order_optional); #endif GListPtr lpc2 = NULL; for(lpc2 = clone_stop->actions_before; lpc2 != NULL; lpc2 = lpc2->next) { action_wrapper_t *other_w = (action_wrapper_t*)lpc2->data; /* Needed if the clone's stop pseudo-action ever gets printed in the graph */ if(other_w->action == stop) { crm_debug("Breaking %s -> %s ordering", other_w->action->uuid, clone_stop->uuid); other_w->type = pe_order_none; } } } } } #if 0 /* Implied now that start/stop are not morphed into migrate ops */ /* Anything that needed stop to complete, now also needs start to have completed */ for(gIter = stop->actions_after; gIter != NULL; gIter = gIter->next) { action_wrapper_t *other_w = (action_wrapper_t*)gIter->data; other = other_w->action; if(is_set(other->flags, pe_action_optional) || other->rsc != NULL) { continue; } crm_debug("Ordering %s before %s (stop)", from->uuid, other->uuid); order_actions(from, other, other_w->type); } #endif /* migrate_to also needs anything that the stop needed to have completed too */ for(gIter = stop->actions_before; gIter != NULL; gIter = gIter->next) { action_wrapper_t *other_w = (action_wrapper_t*)gIter->data; other = other_w->action; if(other->rsc == NULL) { /* nothing */ } else if(is_set(other->flags, pe_action_optional) || other->rsc == rsc || other->rsc == rsc->parent) { continue; } crm_debug("Ordering %s before %s (stop)", other_w->action->uuid, stop->uuid); order_actions(other, to, other_w->type); } /* migrate_to also needs anything that the start needed to have completed too */ for(gIter = start->actions_before; gIter != NULL; gIter = gIter->next) { action_wrapper_t *other_w = (action_wrapper_t*)gIter->data; other = other_w->action; if(other->rsc == NULL) { /* nothing */ } else if(is_set(other->flags, pe_action_optional) || other->rsc == rsc || other->rsc == rsc->parent) { continue; } crm_debug("Ordering %s before %s (start)", other_w->action->uuid, stop->uuid); order_actions(other, to, other_w->type); } } else if(start && stop && is_set(start->flags, pe_action_allow_reload_conversion) && stop->node->details == start->node->details) { action_t *rewrite = NULL; update_action_flags(start, pe_action_pseudo); /* easier than trying to delete it from the graph */ action = NULL; key = promote_key(rsc); action_list = find_actions(rsc->actions, key, NULL); if(action_list) { action = action_list->data; } if(action && is_set(action->flags, pe_action_optional) == FALSE) { update_action_flags(action, pe_action_pseudo); } g_list_free(action_list); crm_free(key); action = NULL; key = demote_key(rsc); action_list = find_actions(rsc->actions, key, NULL); if(action_list) { action = action_list->data; } g_list_free(action_list); crm_free(key); if(action && is_set(action->flags, pe_action_optional) == FALSE) { rewrite = action; update_action_flags(stop, pe_action_pseudo); } else { rewrite = stop; } crm_info("Rewriting %s of %s on %s as a reload", rewrite->task, rsc->id, stop->node->details->uname); crm_free(rewrite->uuid); crm_free(rewrite->task); rewrite->task = crm_strdup("reload"); rewrite->uuid = generate_op_key(rsc->id, rewrite->task, 0); } else { do_crm_log_unlikely(level+1, "%s nothing to do", rsc->id); } } void native_append_meta(resource_t *rsc, xmlNode *xml) { char *value = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_INCARNATION); if(value) { char *name = NULL; name = crm_meta_name(XML_RSC_ATTR_INCARNATION); crm_xml_add(xml, name, value); crm_free(name); } } diff --git a/pengine/utils.c b/pengine/utils.c index d1ff4cd93c..8b77f44d14 100644 --- a/pengine/utils.c +++ b/pengine/utils.c @@ -1,764 +1,764 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include void print_rsc_to_node(const char *pre_text, rsc_to_node_t *cons, gboolean details) { if(cons == NULL) { crm_debug_4("%s%s: ", pre_text==NULL?"":pre_text, pre_text==NULL?"":": "); return; } crm_debug_4("%s%s%s Constraint %s (%p) - %d nodes:", pre_text==NULL?"":pre_text, pre_text==NULL?"":": ", "rsc_to_node", cons->id, cons, g_list_length(cons->node_list_rh)); if(details == FALSE) { GListPtr gIter = NULL; crm_debug_4("\t%s (node placement rule)", safe_val3(NULL, cons, rsc_lh, id)); gIter = cons->node_list_rh; for(; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t*)gIter->data; print_node("\t\t-->", node, FALSE); } } } void print_rsc_colocation(const char *pre_text, rsc_colocation_t *cons, gboolean details) { if(cons == NULL) { crm_debug_4("%s%s: ", pre_text==NULL?"":pre_text, pre_text==NULL?"":": "); return; } crm_debug_4("%s%s%s Constraint %s (%p):", pre_text==NULL?"":pre_text, pre_text==NULL?"":": ", XML_CONS_TAG_RSC_DEPEND, cons->id, cons); if(details == FALSE) { crm_debug_4("\t%s --> %s, %d", safe_val3(NULL, cons, rsc_lh, id), safe_val3(NULL, cons, rsc_rh, id), cons->score); } } void pe_free_ordering(GListPtr constraints) { GListPtr iterator = constraints; while(iterator != NULL) { order_constraint_t *order = iterator->data; iterator = iterator->next; crm_free(order->lh_action_task); crm_free(order->rh_action_task); crm_free(order); } if(constraints != NULL) { g_list_free(constraints); } } void pe_free_rsc_to_node(GListPtr constraints) { GListPtr iterator = constraints; while(iterator != NULL) { rsc_to_node_t *cons = iterator->data; iterator = iterator->next; - pe_free_shallow(cons->node_list_rh); + slist_basic_destroy(cons->node_list_rh); crm_free(cons); } if(constraints != NULL) { g_list_free(constraints); } } rsc_to_node_t * rsc2node_new(const char *id, resource_t *rsc, int node_weight, node_t *foo_node, pe_working_set_t *data_set) { rsc_to_node_t *new_con = NULL; if(rsc == NULL || id == NULL) { pe_err("Invalid constraint %s for rsc=%p", crm_str(id), rsc); return NULL; } else if(foo_node == NULL) { CRM_CHECK(node_weight == 0, return NULL); } crm_malloc0(new_con, sizeof(rsc_to_node_t)); if(new_con != NULL) { new_con->id = id; new_con->rsc_lh = rsc; new_con->node_list_rh = NULL; new_con->role_filter = RSC_ROLE_UNKNOWN; if(foo_node != NULL) { node_t *copy = node_copy(foo_node); copy->weight = merge_weights(node_weight, foo_node->weight); new_con->node_list_rh = g_list_prepend(NULL, copy); } data_set->placement_constraints = g_list_prepend( data_set->placement_constraints, new_con); rsc->rsc_location = g_list_prepend(rsc->rsc_location, new_con); } return new_con; } const char * ordering_type2text(enum pe_ordering type) { const char *result = ""; if(type & pe_order_optional) { /* was: mandatory */ result = "right_implies_left"; } else if(type & pe_order_implies_then) { /* was: recover */ result = "left_implies_right"; } else if(type & pe_order_optional) { /* pure ordering, nothing implied */ result = "optional"; } else if(type & pe_order_runnable_left) { result = "runnable"; /* } else { */ /* crm_err("Unknown ordering type: %.3x", type); */ } return result; } gboolean can_run_resources(const node_t *node) { if(node == NULL) { return FALSE; } #if 0 if(node->weight < 0) { return FALSE; } #endif if(node->details->online == FALSE || node->details->shutdown || node->details->unclean || node->details->standby) { crm_debug_2("%s: online=%d, unclean=%d, standby=%d", node->details->uname, node->details->online, node->details->unclean, node->details->standby); return FALSE; } return TRUE; } struct compare_data { const node_t *node1; const node_t *node2; int result; }; static void do_compare_capacity1(gpointer key, gpointer value, gpointer user_data) { int node1_capacity = 0; int node2_capacity = 0; struct compare_data *data = user_data; node1_capacity = crm_parse_int(value, "0"); node2_capacity = crm_parse_int(g_hash_table_lookup(data->node2->details->utilization, key), "0"); if (node1_capacity > node2_capacity) { data->result--; } else if (node1_capacity < node2_capacity) { data->result++; } } static void do_compare_capacity2(gpointer key, gpointer value, gpointer user_data) { int node1_capacity = 0; int node2_capacity = 0; struct compare_data *data = user_data; if (g_hash_table_lookup_extended(data->node1->details->utilization, key, NULL, NULL)) { return; } node1_capacity = 0; node2_capacity = crm_parse_int(value, "0"); if (node1_capacity > node2_capacity) { data->result--; } else if (node1_capacity < node2_capacity) { data->result++; } } /* rc < 0 if 'node1' has more capacity remaining * rc > 0 if 'node1' has less capacity remaining */ static int compare_capacity(const node_t *node1, const node_t *node2) { struct compare_data data; data.node1 = node1; data.node2 = node2; data.result = 0; g_hash_table_foreach(node1->details->utilization, do_compare_capacity1, &data); g_hash_table_foreach(node2->details->utilization, do_compare_capacity2, &data); return data.result; } /* return -1 if 'a' is more preferred * return 1 if 'b' is more preferred */ gint sort_node_weight(gconstpointer a, gconstpointer b, gpointer data) { int level = LOG_DEBUG_3; const node_t *node1 = (const node_t*)a; const node_t *node2 = (const node_t*)b; const node_t *active = (node_t*)data; int node1_weight = 0; int node2_weight = 0; int result = 0; if(a == NULL) { return 1; } if(b == NULL) { return -1; } node1_weight = node1->weight; node2_weight = node2->weight; if(can_run_resources(node1) == FALSE) { node1_weight = -INFINITY; } if(can_run_resources(node2) == FALSE) { node2_weight = -INFINITY; } if(node1_weight > node2_weight) { do_crm_log_unlikely(level, "%s (%d) > %s (%d) : weight", node1->details->uname, node1_weight, node2->details->uname, node2_weight); return -1; } if(node1_weight < node2_weight) { do_crm_log_unlikely(level, "%s (%d) < %s (%d) : weight", node1->details->uname, node1_weight, node2->details->uname, node2_weight); return 1; } do_crm_log_unlikely(level, "%s (%d) == %s (%d) : weight", node1->details->uname, node1_weight, node2->details->uname, node2_weight); if (safe_str_eq(pe_dataset->placement_strategy, "minimal")) { goto equal; } if (safe_str_eq(pe_dataset->placement_strategy, "balanced")) { result = compare_capacity(node1, node2); if (result != 0) { return result; } } /* now try to balance resources across the cluster */ if(node1->details->num_resources < node2->details->num_resources) { do_crm_log_unlikely(level, "%s (%d) < %s (%d) : resources", node1->details->uname, node1->details->num_resources, node2->details->uname, node2->details->num_resources); return -1; } else if(node1->details->num_resources > node2->details->num_resources) { do_crm_log_unlikely(level, "%s (%d) > %s (%d) : resources", node1->details->uname, node1->details->num_resources, node2->details->uname, node2->details->num_resources); return 1; } if(active && active->details == node1->details) { do_crm_log_unlikely(level, "%s (%d) > %s (%d) : active", node1->details->uname, node1->details->num_resources, node2->details->uname, node2->details->num_resources); return -1; } else if(active && active->details == node2->details) { do_crm_log_unlikely(level, "%s (%d) > %s (%d) : active", node1->details->uname, node1->details->num_resources, node2->details->uname, node2->details->num_resources); return 1; } equal: do_crm_log_unlikely(level, "%s = %s", node1->details->uname, node2->details->uname); return strcmp(node1->details->uname, node2->details->uname); } struct calculate_data { node_t *node; gboolean allocate; }; static void do_calculate_utilization(gpointer key, gpointer value, gpointer user_data) { const char *capacity = NULL; char *remain_capacity = NULL; struct calculate_data *data = user_data; capacity = g_hash_table_lookup(data->node->details->utilization, key); if (capacity) { if (data->allocate) { remain_capacity = crm_itoa(crm_parse_int(capacity, "0") - crm_parse_int(value, "0")); } else { remain_capacity = crm_itoa(crm_parse_int(capacity, "0") + crm_parse_int(value, "0")); } g_hash_table_replace(data->node->details->utilization, crm_strdup(key), remain_capacity); } } /* Specify 'allocate' to TRUE when allocating * Otherwise to FALSE when deallocating */ static void calculate_utilization(node_t *node, resource_t *rsc, gboolean allocate) { struct calculate_data data; data.node = node; data.allocate = allocate; g_hash_table_foreach(rsc->utilization, do_calculate_utilization, &data); if (allocate) { dump_rsc_utilization(show_utilization?0:utilization_log_level, __FUNCTION__, rsc, node); } } gboolean native_assign_node(resource_t *rsc, GListPtr nodes, node_t *chosen, gboolean force) { CRM_ASSERT(rsc->variant == pe_native); clear_bit(rsc->flags, pe_rsc_provisional); if(force == FALSE && chosen != NULL && (can_run_resources(chosen) == FALSE || chosen->weight < 0)) { crm_debug("All nodes for resource %s are unavailable" ", unclean or shutting down (%s: %d, %d)", rsc->id, chosen->details->uname, can_run_resources(chosen), chosen->weight); rsc->next_role = RSC_ROLE_STOPPED; chosen = NULL; } /* todo: update the old node for each resource to reflect its * new resource count */ if(rsc->allocated_to) { node_t *old = rsc->allocated_to; crm_info("Deallocating %s from %s", rsc->id, old->details->uname); old->details->allocated_rsc = g_list_remove( old->details->allocated_rsc, rsc); old->details->num_resources--; old->count--; calculate_utilization(old, rsc, FALSE); } if(chosen == NULL) { char *key = NULL; GListPtr gIter = NULL; GListPtr possible_matches = NULL; crm_debug("Could not allocate a node for %s", rsc->id); rsc->next_role = RSC_ROLE_STOPPED; key = generate_op_key(rsc->id, CRMD_ACTION_STOP, 0); possible_matches = find_actions(rsc->actions, key, NULL); gIter = possible_matches; for(; gIter != NULL; gIter = gIter->next) { action_t *stop = (action_t*)gIter->data; update_action_flags(stop, pe_action_optional|pe_action_clear); } g_list_free(possible_matches); crm_free(key); key = generate_op_key(rsc->id, CRMD_ACTION_START, 0); possible_matches = find_actions(rsc->actions, key, NULL); gIter = possible_matches; for(; gIter != NULL; gIter = gIter->next) { action_t *start = (action_t*)gIter->data; update_action_flags(start, pe_action_runnable|pe_action_clear); } g_list_free(possible_matches); crm_free(key); return FALSE; } crm_debug("Assigning %s to %s", chosen->details->uname, rsc->id); crm_free(rsc->allocated_to); rsc->allocated_to = node_copy(chosen); chosen->details->allocated_rsc = g_list_prepend(chosen->details->allocated_rsc, rsc); chosen->details->num_resources++; chosen->count++; calculate_utilization(chosen, rsc, TRUE); return TRUE; } char * convert_non_atomic_uuid(char *old_uuid, resource_t *rsc, gboolean allow_notify, gboolean free_original) { int interval = 0; char *uuid = NULL; char *rid = NULL; char *raw_task = NULL; int task = no_action; crm_debug_3("Processing %s", old_uuid); if(old_uuid == NULL) { return NULL; } else if(strstr(old_uuid, "notify") != NULL) { goto done; /* no conversion */ } else if(rsc->variant < pe_group) { goto done; /* no conversion */ } CRM_ASSERT(parse_op_key(old_uuid, &rid, &raw_task, &interval)); if(interval > 0) { goto done; /* no conversion */ } task = text2task(raw_task); switch(task) { case stop_rsc: case start_rsc: case action_notify: case action_promote: case action_demote: break; case stopped_rsc: case started_rsc: case action_notified: case action_promoted: case action_demoted: task--; break; case monitor_rsc: case shutdown_crm: case stonith_node: task = no_action; break; default: crm_err("Unknown action: %s", raw_task); task = no_action; break; } if(task != no_action) { if(is_set(rsc->flags, pe_rsc_notify) && allow_notify) { uuid = generate_notify_key(rid, "confirmed-post", task2text(task+1)); } else { uuid = generate_op_key(rid, task2text(task+1), 0); } crm_debug_2("Converted %s -> %s", old_uuid, uuid); } done: if(uuid == NULL) { uuid = crm_strdup(old_uuid); } if(free_original) { crm_free(old_uuid); } crm_free(raw_task); crm_free(rid); return uuid; } gboolean order_actions( action_t *lh_action, action_t *rh_action, enum pe_ordering order) { GListPtr gIter = NULL; action_wrapper_t *wrapper = NULL; GListPtr list = NULL; static int load_stopped_strlen = 0; if(order == pe_order_none) { return FALSE; } if (!load_stopped_strlen) { load_stopped_strlen = strlen(LOAD_STOPPED); } if (strncmp(lh_action->uuid, LOAD_STOPPED, load_stopped_strlen) == 0 || strncmp(rh_action->uuid, LOAD_STOPPED, load_stopped_strlen) == 0) { if (lh_action->node == NULL || rh_action->node == NULL || lh_action->node->details != rh_action->node->details) { return FALSE; } } crm_debug_3("Ordering Action %s before %s", lh_action->uuid, rh_action->uuid); log_action(LOG_DEBUG_4, "LH (order_actions)", lh_action, FALSE); log_action(LOG_DEBUG_4, "RH (order_actions)", rh_action, FALSE); /* Filter dups, otherwise update_action_states() has too much work to do */ gIter = lh_action->actions_after; for(; gIter != NULL; gIter = gIter->next) { action_wrapper_t *after = (action_wrapper_t*)gIter->data; if(after->action == rh_action && (after->type & order)) { return FALSE; } } crm_malloc0(wrapper, sizeof(action_wrapper_t)); wrapper->action = rh_action; wrapper->type = order; list = lh_action->actions_after; list = g_list_prepend(list, wrapper); lh_action->actions_after = list; wrapper = NULL; /* order |= pe_order_implies_then; */ /* order ^= pe_order_implies_then; */ crm_malloc0(wrapper, sizeof(action_wrapper_t)); wrapper->action = lh_action; wrapper->type = order; list = rh_action->actions_before; list = g_list_prepend(list, wrapper); rh_action->actions_before = list; return TRUE; } void log_action(unsigned int log_level, const char *pre_text, action_t *action, gboolean details) { const char *node_uname = NULL; const char *node_uuid = NULL; if(action == NULL) { do_crm_log_unlikely(log_level, "%s%s: ", pre_text==NULL?"":pre_text, pre_text==NULL?"":": "); return; } if(is_set(action->flags, pe_action_pseudo)) { node_uname = NULL; node_uuid = NULL; } else if(action->node != NULL) { node_uname = action->node->details->uname; node_uuid = action->node->details->id; } else { node_uname = ""; node_uuid = NULL; } switch(text2task(action->task)) { case stonith_node: case shutdown_crm: do_crm_log_unlikely(log_level, "%s%s%sAction %d: %s%s%s%s%s%s", pre_text==NULL?"":pre_text, pre_text==NULL?"":": ", is_set(action->flags, pe_action_pseudo)?"Pseduo ":is_set(action->flags, pe_action_optional)?"Optional ":is_set(action->flags, pe_action_runnable)?is_set(action->flags, pe_action_processed)?"":"(Provisional) ":"!!Non-Startable!! ", action->id, action->uuid, node_uname?"\ton ":"", node_uname?node_uname:"", node_uuid?"\t\t(":"", node_uuid?node_uuid:"", node_uuid?")":""); break; default: do_crm_log_unlikely(log_level, "%s%s%sAction %d: %s %s%s%s%s%s%s", pre_text==NULL?"":pre_text, pre_text==NULL?"":": ", is_set(action->flags, pe_action_optional)?"Optional ":is_set(action->flags, pe_action_pseudo)?"Pseduo ":is_set(action->flags, pe_action_runnable)?is_set(action->flags, pe_action_processed)?"":"(Provisional) ":"!!Non-Startable!! ", action->id, action->uuid, safe_val3("", action, rsc, id), node_uname?"\ton ":"", node_uname?node_uname:"", node_uuid?"\t\t(":"", node_uuid?node_uuid:"", node_uuid?")":""); break; } if(details) { GListPtr gIter = NULL; do_crm_log_unlikely(log_level+1, "\t\t====== Preceding Actions"); gIter = action->actions_before; for(; gIter != NULL; gIter = gIter->next) { action_wrapper_t *other = (action_wrapper_t*)gIter->data; log_action(log_level+1, "\t\t", other->action, FALSE); } do_crm_log_unlikely(log_level+1, "\t\t====== Subsequent Actions"); gIter = action->actions_after; for(; gIter != NULL; gIter = gIter->next) { action_wrapper_t *other = (action_wrapper_t*)gIter->data; log_action(log_level+1, "\t\t", other->action, FALSE); } do_crm_log_unlikely(log_level+1, "\t\t====== End"); } else { do_crm_log_unlikely(log_level, "\t\t(seen=%d, before=%d, after=%d)", action->seen_count, g_list_length(action->actions_before), g_list_length(action->actions_after)); } } action_t *get_pseudo_op(const char *name, pe_working_set_t *data_set) { action_t *op = NULL; const char *op_s = name; GListPtr possible_matches = NULL; possible_matches = find_actions(data_set->actions, name, NULL); if(possible_matches != NULL) { if(g_list_length(possible_matches) > 1) { pe_warn("Action %s exists %d times", name, g_list_length(possible_matches)); } op = g_list_nth_data(possible_matches, 0); g_list_free(possible_matches); } else { op = custom_action(NULL, crm_strdup(op_s), op_s, NULL, TRUE, TRUE, data_set); update_action_flags(op, pe_action_pseudo); update_action_flags(op, pe_action_runnable); } return op; } gboolean can_run_any(GHashTable *nodes) { GHashTableIter iter; node_t *node = NULL; if(nodes == NULL) { return FALSE; } g_hash_table_iter_init (&iter, nodes); while (g_hash_table_iter_next (&iter, NULL, (void**)&node)) { if(can_run_resources(node) && node->weight >= 0) { return TRUE; } } return FALSE; } enum rsc_role_e minimum_resource_state(resource_t *rsc, gboolean current) { enum rsc_role_e min_role = RSC_ROLE_MAX; if(rsc->children) { GListPtr gIter = rsc->children; for(; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t*)gIter->data; enum rsc_role_e role = child_rsc->fns->state(child_rsc, current); if(role < min_role) { min_role = role; } } return min_role; } return rsc->fns->state(rsc, current); } diff --git a/tools/cib_shadow.c b/tools/cib_shadow.c index e4ba6526ae..3154482753 100644 --- a/tools/cib_shadow.c +++ b/tools/cib_shadow.c @@ -1,591 +1,590 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include int exit_code = cib_ok; GMainLoop *mainloop = NULL; IPC_Channel *crmd_channel = NULL; const char *host = NULL; void usage(const char *cmd, int exit_status); int command_options = cib_sync_call; const char *cib_action = NULL; cib_t *real_cib = NULL; int dump_data_element( int depth, char **buffer, int *max, int *offset, const char *prefix, xmlNode *data, gboolean formatted); void print_xml_diff(FILE *where, xmlNode *diff); static int force_flag = 0; static int batch_flag = 0; static char *get_shadow_prompt(const char *name) { int len = 16; char *prompt = NULL; CRM_ASSERT(name != NULL); len += strlen(name); crm_malloc0(prompt, len); snprintf(prompt, len, "shadow[%s] # ", name); return prompt; } static void shadow_setup(char *name, gboolean do_switch) { const char *prompt = getenv("PS1"); const char *shell = getenv("SHELL"); char *new_prompt = get_shadow_prompt(name); printf("Setting up shadow instance\n"); if(safe_str_eq(new_prompt, prompt)) { /* nothing to do */ goto done; } else if(batch_flag == FALSE && shell != NULL) { setenv("PS1", new_prompt, 1); setenv("CIB_shadow", name, 1); printf("Type Ctrl-D to exit the crm_shadow shell\n"); execl(shell, "--norc", "--noprofile", NULL); } else if (do_switch) { printf("To switch to the named shadow instance, paste the following into your shell:\n"); } else { printf("A new shadow instance was created. To begin using it paste the following into your shell:\n"); } printf(" CIB_shadow=%s ; export CIB_shadow\n", name); done: crm_free(new_prompt); } static void shadow_teardown(char *name) { const char *prompt = getenv("PS1"); char *our_prompt = get_shadow_prompt(name); if(prompt != NULL && strstr(prompt, our_prompt)) { printf("Now type Ctrl-D to exit the crm_shadow shell\n"); } else { printf("Please remember to unset the CIB_shadow variable by pasting the following into your shell:\n"); printf(" unset CIB_shadow\n"); } crm_free(our_prompt); } static struct crm_option long_options[] = { /* Top-level Options */ {"help", 0, 0, '?', "\t\tThis text"}, {"version", 0, 0, '$', "\t\tVersion information" }, {"verbose", 0, 0, 'V', "\t\tIncrease debug output"}, {"-spacer-", 1, 0, '-', "\nQueries:"}, {"which", no_argument, NULL, 'w', "\t\tIndicate the active shadow copy"}, {"display", no_argument, NULL, 'p', "\t\tDisplay the contents of the active shadow copy"}, {"edit", no_argument, NULL, 'E', "\t\tEdit the contents of the active shadow copy with your favorite $EDITOR"}, {"diff", no_argument, NULL, 'd', "\t\tDisplay the changes in the active shadow copy\n"}, {"file", no_argument, NULL, 'F', "\t\tDisplay the location of the active shadow copy file\n"}, {"-spacer-", 1, 0, '-', "\nCommands:"}, {"create", required_argument, NULL, 'c', "\tCreate the named shadow copy of the active cluster configuration"}, {"create-empty", required_argument, NULL, 'e', "Create the named shadow copy with an empty cluster configuration"}, {"commit", required_argument, NULL, 'C', "\tUpload the contents of the named shadow copy to the cluster"}, {"delete", required_argument, NULL, 'D', "\tDelete the contents of the named shadow copy"}, {"reset", required_argument, NULL, 'r', "\tRecreate the named shadow copy from the active cluster configuration"}, {"switch", required_argument, NULL, 's', "\t(Advanced) Switch to the named shadow copy"}, {"-spacer-", 1, 0, '-', "\nAdditional Options:"}, {"force", no_argument, NULL, 'f', "\t\t(Advanced) Force the action to be performed"}, {"batch", no_argument, NULL, 'b', "\t\t(Advanced) Don't spawn a new shell" }, {"all", no_argument, NULL, 'a', "\t\t(Advanced) Upload the entire CIB, including status, with --commit" }, {"-spacer-", 1, 0, '-', "\nExamples:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', "Create a blank shadow configuration:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_shadow --create-empty myShadow", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Create a shadow configuration from the running cluster:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_shadow --create myShadow", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Display the current shadow configuration:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_shadow --display", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Discard the current shadow configuration (named myShadow):", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_shadow --delete myShadow", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Upload the current shadow configuration (named myShadow) to the running cluster:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_shadow --commit myShadow", pcmk_option_example}, {0, 0, 0, 0} }; int main(int argc, char **argv) { int rc = 0; int flag; int argerr = 0; static int command = '?'; char *shadow = NULL; char *shadow_file = NULL; gboolean full_upload = FALSE; gboolean dangerous_cmd = FALSE; struct stat buf; int option_index = 0; crm_log_init("crm_shadow", LOG_CRIT, FALSE, FALSE, argc, argv); crm_set_options("V$?bfwc:dr:C:D:ps:Ee:Fa", "(query|command) [modifiers]", long_options, "Perform configuration changes in a sandbox before updating the live cluster." "\n\nSets up an environment in which configuration tools (cibadmin, crm_resource, etc) work" " offline instead of against a live cluster, allowing changes to be previewed and tested" " for side-effects.\n"); if(argc < 2) { crm_help('?', LSB_EXIT_EINVAL); } while (1) { flag = crm_get_option(argc, argv, &option_index); if (flag == -1 || flag == 0) break; switch(flag) { case 'd': case 'E': case 'p': case 'w': case 'F': command = flag; crm_free(shadow); shadow = crm_strdup(getenv("CIB_shadow")); break; case 'e': case 'c': case 's': case 'r': command = flag; crm_free(shadow); shadow = crm_strdup(optarg); break; case 'C': case 'D': command = flag; dangerous_cmd = TRUE; crm_free(shadow); shadow = crm_strdup(optarg); break; case 'V': command_options = command_options | cib_verbose; cl_log_enable_stderr(TRUE); alter_debug(DEBUG_INC); break; case '$': case '?': crm_help(flag, LSB_EXIT_OK); break; case 'f': command_options |= cib_quorum_override; force_flag = 1; break; case 'b': batch_flag = 1; break; default: printf("Argument code 0%o (%c)" " is not (?yet?) supported\n", flag, flag); ++argerr; break; } } if (optind < argc) { printf("non-option ARGV-elements: "); while (optind < argc) printf("%s ", argv[optind++]); printf("\n"); crm_help('?', LSB_EXIT_EINVAL); } if (optind > argc) { ++argerr; } if (argerr) { crm_help('?', LSB_EXIT_GENERIC); } if(command == 'w') { /* which shadow instance is active? */ const char *local = getenv("CIB_shadow"); if(local == NULL) { fprintf(stderr, "No shadow instance provided\n"); rc = cib_NOTEXISTS; goto done; } fprintf(stdout, "%s\n", local); rc = 0; goto done; } if(shadow == NULL) { fprintf(stderr, "No shadow instance provided\n"); fflush(stderr); rc = CIBRES_MISSING_FIELD; goto done; } else if(command != 's' && command != 'c') { const char *local = getenv("CIB_shadow"); if(local != NULL && safe_str_neq(local, shadow) && force_flag == FALSE) { fprintf(stderr, "The supplied shadow instance (%s) is not the same as the active one (%s).\n" " To prevent accidental destruction of the cluster," " the --force flag is required in order to proceed.\n", shadow, local); fflush(stderr); rc = LSB_EXIT_GENERIC; goto done; } } if(dangerous_cmd && force_flag == FALSE) { fprintf(stderr, "The supplied command is considered dangerous." " To prevent accidental destruction of the cluster," " the --force flag is required in order to proceed.\n"); fflush(stderr); rc = LSB_EXIT_GENERIC; goto done; } shadow_file = get_shadow_file(shadow); if(command == 'D') { /* delete the file */ rc = stat(shadow_file, &buf); if(rc == 0) { rc = unlink(shadow_file); if(rc != 0) { fprintf(stderr, "Could not remove shadow instance '%s': %s\n", shadow, strerror(errno)); goto done; } } shadow_teardown(shadow); goto done; } else if(command == 'F') { printf("%s\n", shadow_file); rc = 0; goto done; } if(command == 'd' || command == 'r' || command == 'c' || command == 'C') { real_cib = cib_new_no_shadow(); rc = real_cib->cmds->signon(real_cib, crm_system_name, cib_command); if(rc != cib_ok) { fprintf(stderr, "Signon to CIB failed: %s\n", cib_error2string(rc)); goto done; } } rc = stat(shadow_file, &buf); if(command == 'e' || command == 'c') { if (rc == 0 && force_flag == FALSE) { fprintf(stderr, "A shadow instance '%s' already exists.\n" - " To prevent accidental destruction of the cluster," - " the --force flag is required in order to proceed.\n", shadow); + " To prevent accidental destruction of the cluster," + " the --force flag is required in order to proceed.\n", shadow); rc = cib_EXISTS; goto done; } } else if(rc != 0) { fprintf(stderr, "Could not access shadow instance '%s': %s\n", shadow, strerror(errno)); rc = cib_NOTEXISTS; goto done; } rc = cib_ok; if(command == 'c' || command == 'e') { xmlNode *output = NULL; /* create a shadow instance based on the current cluster config */ if(command == 'c') { rc = real_cib->cmds->query(real_cib, NULL, &output, command_options); if(rc != cib_ok) { fprintf(stderr, "Could not connect to the CIB: %s\n", cib_error2string(rc)); goto done; } } else { output = createEmptyCib(); crm_xml_add(output, XML_ATTR_GENERATION, "0"); crm_xml_add(output, XML_ATTR_NUMUPDATES, "0"); crm_xml_add(output, XML_ATTR_GENERATION_ADMIN, "0"); crm_xml_add(output, XML_ATTR_VALIDATION, LATEST_SCHEMA_VERSION); } rc = write_xml_file(output, shadow_file, FALSE); free_xml(output); if(rc < 0) { fprintf(stderr, "Could not create the shadow instance '%s': %s\n", shadow, strerror(errno)); goto done; } shadow_setup(shadow, FALSE); rc = cib_ok; } else if(command == 'E') { const char *err = NULL; char *editor = getenv("EDITOR"); if(editor == NULL) { fprintf(stderr, "No value for $EDITOR defined\n"); rc = cib_missing; goto done; } execlp(editor, "--", shadow_file, NULL); err = strerror(errno); fprintf(stderr, "Could not invoke $EDITOR (%s %s): %s\n", editor, shadow_file, err); rc = cib_missing; goto done; } else if(command == 's') { shadow_setup(shadow, TRUE); rc = 0; goto done; } else if(command == 'P') { /* display the current contents */ char *output_s = NULL; xmlNode *output = filename2xml(shadow_file); output_s = dump_xml_formatted(output); printf("%s", output_s); crm_free(output_s); free_xml(output); } else if(command == 'd') { /* diff against cluster */ xmlNode *diff = NULL; xmlNode *old_config = NULL; xmlNode *new_config = filename2xml(shadow_file); rc = real_cib->cmds->query(real_cib, NULL, &old_config, command_options); if(rc != cib_ok) { fprintf(stderr, "Could not query the CIB: %s\n", cib_error2string(rc)); goto done; } diff = diff_xml_object(old_config, new_config, FALSE); if(diff != NULL) { print_xml_diff(stdout, diff); rc = 1; goto done; } rc = 0; goto done; } else if(command == 'C') { /* commit to the cluster */ xmlNode *input = filename2xml(shadow_file); if(full_upload) { rc = real_cib->cmds->replace(real_cib, NULL, input, command_options); } else { xmlNode *config = first_named_child(input, XML_CIB_TAG_CONFIGURATION); rc = real_cib->cmds->replace(real_cib, XML_CIB_TAG_CONFIGURATION, config, command_options); } if(rc != cib_ok) { fprintf(stderr, "Could not commit shadow instance '%s' to the CIB: %s\n", shadow, cib_error2string(rc)); return rc; } shadow_teardown(shadow); free_xml(input); } done: crm_xml_cleanup(); crm_free(shadow_file); crm_free(shadow); return rc; } #define bhead(buffer, offset) ((*buffer) + (*offset)) #define bremain(max, offset) ((*max) - (*offset)) -#define update_buffer_head(len) do { \ - int total = (*offset) + len + 1; \ - if(total >= (*max)) { /* too late */ \ - (*buffer) = EOS; return -1; \ - } else if(((*max) - total) < 256) { \ - (*max) *= 10; \ - crm_realloc(*buffer, (*max)); \ - } \ - (*offset) += len; \ +#define update_buffer_head(len) do { \ + int total = (*offset) + len + 1; \ + if(total >= (*max)) { /* too late */ \ + (*buffer) = EOS; return -1; \ + } else if(((*max) - total) < 256) { \ + (*max) *= 10; \ + crm_realloc(*buffer, (*max)); \ + } \ + (*offset) += len; \ } while(0) extern int print_spaces(char *buffer, int depth, int max); int dump_data_element( int depth, char **buffer, int *max, int *offset, const char *prefix, xmlNode *data, gboolean formatted) { int printed = 0; int has_children = 0; + xmlNode *child = NULL; const char *name = NULL; CRM_CHECK(data != NULL, return 0); name = crm_element_name(data); CRM_CHECK(name != NULL, return 0); CRM_CHECK(buffer != NULL && *buffer != NULL, return 0); crm_debug_5("Dumping %s...", name); if(prefix) { printed = snprintf(bhead(buffer, offset), bremain(max, offset), "%s", prefix); update_buffer_head(printed); } if(formatted) { printed = print_spaces(bhead(buffer, offset), depth, bremain(max, offset)); update_buffer_head(printed); } printed = snprintf(bhead(buffer, offset), bremain(max, offset), "<%s", name); update_buffer_head(printed); xml_prop_iter(data, prop_name, prop_value, crm_debug_5("Dumping <%s %s=\"%s\"...", name, prop_name, prop_value); printed = snprintf(bhead(buffer, offset), bremain(max, offset), " %s=\"%s\"", prop_name, prop_value); update_buffer_head(printed); ); has_children = xml_has_children(data); printed = snprintf(bhead(buffer, offset), bremain(max, offset), "%s>%s", has_children==0?"/":"", formatted?"\n":""); update_buffer_head(printed); if(has_children == 0) { return 0; } - xml_child_iter(data, child, - if(dump_data_element(depth+1, buffer, max, offset, prefix, child, formatted) < 0) { - return -1; - } - ); + for(child = data; child != NULL; child = child->next) { + if(dump_data_element(depth+1, buffer, max, offset, prefix, child, formatted) < 0) { + return -1; + } + } if(prefix) { printed = snprintf(bhead(buffer, offset), bremain(max, offset), "%s", prefix); update_buffer_head(printed); } if(formatted) { printed = print_spaces(bhead(buffer, offset), depth, bremain(max, offset)); update_buffer_head(printed); } printed = snprintf(bhead(buffer, offset), bremain(max, offset), "%s", name, formatted?"\n":""); update_buffer_head(printed); crm_debug_5("Dumped %s...", name); return has_children; } void print_xml_diff(FILE *where, xmlNode *diff) { - char *buffer = NULL; - int max = 1024, len = 0; - gboolean is_first = TRUE; - xmlNode *added = find_xml_node(diff, "diff-added", FALSE); - xmlNode *removed = find_xml_node(diff, "diff-removed", FALSE); - - is_first = TRUE; - xml_child_iter( - removed, child, - - len = 0; - max = 1024; - crm_free(buffer); - crm_malloc0(buffer, max); - - if(is_first) { - is_first = FALSE; - } else { - fprintf(where, " --- \n"); - } - - CRM_CHECK(dump_data_element( - 0, &buffer, &max, &len, "-", child, TRUE) >= 0, - continue); - fprintf(where, "%s", buffer); - ); - - is_first = TRUE; - xml_child_iter( - added, child, - - len = 0; - max = 1024; - crm_free(buffer); - crm_malloc0(buffer, max); - - if(is_first) { - is_first = FALSE; - } else { - fprintf(where, " +++ \n"); - } - - CRM_CHECK(dump_data_element( - 0, &buffer, &max, &len, "+", child, TRUE) >= 0, - continue); - fprintf(where, "%s", buffer); - ); + char *buffer = NULL; + xmlNode *child = NULL; + int max = 1024, len = 0; + gboolean is_first = TRUE; + xmlNode *added = find_xml_node(diff, "diff-added", FALSE); + xmlNode *removed = find_xml_node(diff, "diff-removed", FALSE); + + is_first = TRUE; + for(child = removed; child != NULL; child = child->next) { + len = 0; + max = 1024; + crm_free(buffer); + crm_malloc0(buffer, max); + + if(is_first) { + is_first = FALSE; + } else { + fprintf(where, " --- \n"); + } + + CRM_CHECK(dump_data_element( + 0, &buffer, &max, &len, "-", child, TRUE) >= 0, + continue); + fprintf(where, "%s", buffer); + } + + + is_first = TRUE; + for(child = added; child != NULL; child = child->next) { + len = 0; + max = 1024; + crm_free(buffer); + crm_malloc0(buffer, max); + + if(is_first) { + is_first = FALSE; + } else { + fprintf(where, " +++ \n"); + } + + CRM_CHECK(dump_data_element( + 0, &buffer, &max, &len, "+", child, TRUE) >= 0, + continue); + fprintf(where, "%s", buffer); + } } diff --git a/tools/cibadmin.c b/tools/cibadmin.c index d21195ae35..db049790b4 100644 --- a/tools/cibadmin.c +++ b/tools/cibadmin.c @@ -1,572 +1,572 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include int exit_code = cib_ok; int message_timer_id = -1; int message_timeout_ms = 30; GMainLoop *mainloop = NULL; IPC_Channel *crmd_channel = NULL; const char *host = NULL; void usage(const char *cmd, int exit_status); enum cib_errors do_init(void); int do_work(xmlNode *input, int command_options, xmlNode **output); gboolean admin_msg_callback(IPC_Channel * source_data, void *private_data); gboolean admin_message_timeout(gpointer data); void cib_connection_destroy(gpointer user_data); void cibadmin_op_callback(xmlNode *msg, int call_id, int rc, xmlNode *output, void *user_data); int command_options = 0; const char *cib_action = NULL; typedef struct str_list_s { - int num_items; - char *value; - struct str_list_s *next; + int num_items; + char *value; + struct str_list_s *next; } str_list_t; char *obj_type = NULL; char *status = NULL; char *migrate_from = NULL; char *migrate_res = NULL; char *subtype = NULL; char *reset = NULL; int request_id = 0; int operation_status = 0; cib_t *the_cib = NULL; gboolean force_flag = FALSE; static struct crm_option long_options[] = { {"help", 0, 0, '?', "\tThis text"}, {"version", 0, 0, '$', "\tVersion information" }, {"verbose", 0, 0, 'V', "\tIncrease debug output\n"}, {"-spacer-", 0, 0, '-', "Commands:"}, {"upgrade", 0, 0, 'u', "\tUpgrade the configuration to the latest syntax"}, {"query", 0, 0, 'Q', "\tQuery the contents of the CIB"}, {"erase", 0, 0, 'E', "\tErase the contents of the whole CIB"}, {"bump", 0, 0, 'B', "\tIncrease the CIB's epoch value by 1"}, {"create", 0, 0, 'C', "\tCreate an object in the CIB. Will fail if the object already exists."}, {"modify", 0, 0, 'M', "\tFind the object somewhere in the CIB's XML tree and update it. Fails if the object does not exist unless -c is specified"}, {"patch", 0, 0, 'P', "\tSupply an update in the form of an xml diff (See also: crm_diff)"}, {"replace", 0, 0, 'R', "\tRecursivly replace an object in the CIB"}, {"delete", 0, 0, 'D', "\tDelete the first object matching the supplied criteria, Eg. "}, {"-spacer-", 0, 0, '-', "\n\t\t\tThe tagname and all attributes must match in order for the element to be deleted"}, {"delete-all", 0, 0, 'd', "\tWhen used with --xpath, remove all matching objects in the configuration instead of just the first one"}, {"md5-sum", 0, 0, '5', "\tCalculate a CIB digest"}, {"sync", 0, 0, 'S', "\t(Advanced) Force a refresh of the CIB to all nodes\n"}, {"make-slave", 0, 0, 'r', NULL, 1}, {"make-master", 0, 0, 'w', NULL, 1}, {"is-master", 0, 0, 'm', NULL, 1}, {"-spacer-",1, 0, '-', "\nAdditional options:"}, {"force", 0, 0, 'f'}, {"timeout", 1, 0, 't', "Time (in seconds) to wait before declaring the operation failed"}, {"sync-call", 0, 0, 's', "Wait for call to complete before returning"}, {"local", 0, 0, 'l', "\tCommand takes effect locally. Should only be used for queries"}, {"allow-create",0, 0, 'c', "(Advanced) Allow the target of a -M operation to be created if they do not exist"}, {"no-children", 0, 0, 'n', "(Advanced) When querying an object, do not return include its children in the result\n"}, {"no-bcast", 0, 0, 'b', NULL, 1}, {"-spacer-", 0, 0, '-', "Data:"}, {"xml-text", 1, 0, 'X', "Retrieve XML from the supplied string"}, {"xml-file", 1, 0, 'x', "Retrieve XML from the named file"}, {"xml-pipe", 0, 0, 'p', "Retrieve XML from stdin\n"}, {"xpath", 1, 0, 'A', "A valid XPath to use instead of -o"}, {"scope", 1, 0, 'o', "Limit the scope of the operation to a specific section of the CIB."}, {"-spacer-", 0, 0, '-', "\t\t\tValid values are: nodes, resources, constraints, crm_config, rsc_defaults, op_defaults, status"}, {"node", 1, 0, 'N', "(Advanced) Send command to the specified host\n"}, {"-space-", 0, 0, '!', NULL, 1}, {"-spacer-", 0, 0, '-', "\nExamples:\n"}, {"-spacer-", 0, 0, '-', "Query the configuration from the local node:", pcmk_option_paragraph}, {"-spacer-", 0, 0, '-', " cibadmin --query --local", pcmk_option_example}, {"-spacer-", 0, 0, '-', "Query the just the cluster options configuration:", pcmk_option_paragraph}, {"-spacer-", 0, 0, '-', " cibadmin --query --scope crm_config", pcmk_option_example}, {"-spacer-", 0, 0, '-', "Query all 'target-role' settings:", pcmk_option_paragraph}, {"-spacer-", 0, 0, '-', " cibadmin --query --xpath \"//nvpair[@name='target-role']\"", pcmk_option_example}, {"-spacer-", 0, 0, '-', "Remove all 'is-managed' settings:", pcmk_option_paragraph}, {"-spacer-", 0, 0, '-', " cibadmin --delete-all --xpath \"//nvpair[@name='is-managed']\"", pcmk_option_example}, {"-spacer-", 0, 0, '-', "Remove the resource named 'old':", pcmk_option_paragraph}, {"-spacer-", 0, 0, '-', " cibadmin --delete --xml-text ''", pcmk_option_example}, {"-spacer-", 0, 0, '-', "Remove all resources from the configuration:", pcmk_option_paragraph}, {"-spacer-", 0, 0, '-', " cibadmin --replace --scope resources --xml-text ''", pcmk_option_example}, {"-spacer-", 0, 0, '-', "Replace the complete configuration with the contents of $HOME/pacemaker.xml:", pcmk_option_paragraph}, {"-spacer-", 0, 0, '-', " cibadmin --replace --xml-file $HOME/pacemaker.xml", pcmk_option_example}, {"-spacer-", 0, 0, '-', "Replace the constraints section of the configuration with the contents of $HOME/constraints.xml:", pcmk_option_paragraph}, {"-spacer-", 0, 0, '-', " cibadmin --replace --scope constraints --xml-file $HOME/constraints.xml", pcmk_option_example}, {"-spacer-", 0, 0, '-', "Increase the configuration version to prevent old configurations from being loaded accidentally:", pcmk_option_paragraph}, {"-spacer-", 0, 0, '-', " cibadmin --modify --xml-text ''", pcmk_option_example}, {"-spacer-", 0, 0, '-', "Edit the configuration with your favorite $EDITOR:", pcmk_option_paragraph}, {"-spacer-", 0, 0, '-', " cibadmin --query > $HOME/local.xml", pcmk_option_example}, {"-spacer-", 0, 0, '-', " $EDITOR $HOME/local.xml", pcmk_option_example}, {"-spacer-", 0, 0, '-', " cibadmin --replace --xml-file $HOME/local.xml", pcmk_option_example}, {"-spacer-", 0, 0, '-', "SEE ALSO:"}, {"-spacer-", 0, 0, '-', " CRM shell, crm(8), crm_shadow(8)"}, /* Legacy options */ {"host", 0, 0, 'h', NULL, 1}, {"force-quorum", 0, 0, 'f', NULL, 1}, {"obj_type", 1, 0, 'o', NULL, 1}, {F_CRM_DATA, 1, 0, 'X', NULL, 1}, {CIB_OP_ERASE, 0, 0, 'E', NULL, 1}, {CIB_OP_QUERY, 0, 0, 'Q', NULL, 1}, {CIB_OP_CREATE, 0, 0, 'C', NULL, 1}, {CIB_OP_REPLACE, 0, 0, 'R', NULL, 1}, {CIB_OP_UPDATE, 0, 0, 'U', NULL, 1}, {CIB_OP_MODIFY, 0, 0, 'M', NULL, 1}, {CIB_OP_DELETE, 0, 0, 'D', NULL, 1}, {CIB_OP_BUMP, 0, 0, 'B', NULL, 1}, {CIB_OP_SYNC, 0, 0, 'S', NULL, 1}, {CIB_OP_SLAVE, 0, 0, 'r', NULL, 1}, {CIB_OP_MASTER, 0, 0, 'w', NULL, 1}, {CIB_OP_ISMASTER,0, 0, 'm', NULL, 1}, {0, 0, 0, 0} }; int main(int argc, char **argv) { - int argerr = 0; - int flag; - const char *source = NULL; - char *admin_input_xml = NULL; - char *admin_input_file = NULL; - gboolean dangerous_cmd = FALSE; - gboolean admin_input_stdin = FALSE; - xmlNode *output = NULL; - xmlNode *input = NULL; + int argerr = 0; + int flag; + const char *source = NULL; + char *admin_input_xml = NULL; + char *admin_input_file = NULL; + gboolean dangerous_cmd = FALSE; + gboolean admin_input_stdin = FALSE; + xmlNode *output = NULL; + xmlNode *input = NULL; - int option_index = 0; - crm_log_init(NULL, LOG_CRIT, FALSE, FALSE, argc, argv); - crm_set_options("!V?$o:QDUCEX:t:Srwlsh:MmBfbRx:pP5N:A:uncd", "command [options] [data]", long_options, - "Provides direct access to the cluster configuration." - "\n\n Allows the configuration, or sections of it, to be queried, modified, replaced and deleted." - "\n\n Where necessary, XML data will be obtained using the -X, -x, or -p options\n"); - - if(argc < 2) { - crm_help('?',LSB_EXIT_EINVAL); - } - - while (1) { - flag = crm_get_option(argc, argv, &option_index); - if (flag == -1) - break; - - switch(flag) { - case 't': - message_timeout_ms = atoi(optarg); - if(message_timeout_ms < 1) { - message_timeout_ms = 30; - } - break; - case 'A': - obj_type = crm_strdup(optarg); - command_options |= cib_xpath; - break; - case 'u': - cib_action = CIB_OP_UPGRADE; - dangerous_cmd = TRUE; - break; - case 'E': - cib_action = CIB_OP_ERASE; - dangerous_cmd = TRUE; - break; - case 'Q': - cib_action = CIB_OP_QUERY; - break; - case 'P': - cib_action = CIB_OP_APPLY_DIFF; - break; - case 'S': - cib_action = CIB_OP_SYNC; - break; - case 'U': - case 'M': - cib_action = CIB_OP_MODIFY; - break; - case 'R': - cib_action = CIB_OP_REPLACE; - break; - case 'C': - cib_action = CIB_OP_CREATE; - break; - case 'D': - cib_action = CIB_OP_DELETE; - break; - case '5': - cib_action = "md5-sum"; - break; - case 'c': - command_options |= cib_can_create; - break; - case 'n': - command_options |= cib_no_children; - break; - case 'm': - cib_action = CIB_OP_ISMASTER; - command_options |= cib_scope_local; - break; - case 'B': - cib_action = CIB_OP_BUMP; - break; - case 'r': - dangerous_cmd = TRUE; - cib_action = CIB_OP_SLAVE; - break; - case 'w': - dangerous_cmd = TRUE; - cib_action = CIB_OP_MASTER; - command_options |= cib_scope_local; - break; - case 'V': - command_options = command_options | cib_verbose; - cl_log_enable_stderr(TRUE); - alter_debug(DEBUG_INC); - break; - case '?': - case '$': - case '!': - crm_help(flag, LSB_EXIT_OK); - break; - case 'o': - crm_debug_2("Option %c => %s", flag, optarg); - obj_type = crm_strdup(optarg); - break; - case 'X': - crm_debug_2("Option %c => %s", flag, optarg); - admin_input_xml = crm_strdup(optarg); - break; - case 'x': - crm_debug_2("Option %c => %s", flag, optarg); - admin_input_file = crm_strdup(optarg); - break; - case 'p': - admin_input_stdin = TRUE; - break; - case 'h': - host = crm_strdup(optarg); - break; - case 'l': - command_options |= cib_scope_local; - break; - case 'd': - cib_action = CIB_OP_DELETE; - command_options |= cib_multiple; - dangerous_cmd = TRUE; - break; - case 'b': - dangerous_cmd = TRUE; - command_options |= cib_inhibit_bcast; - command_options |= cib_scope_local; - break; - case 's': - command_options |= cib_sync_call; - break; - case 'f': - force_flag = TRUE; - command_options |= cib_quorum_override; - break; - default: - printf("Argument code 0%o (%c)" - " is not (?yet?) supported\n", - flag, flag); - ++argerr; - break; + int option_index = 0; + crm_log_init(NULL, LOG_CRIT, FALSE, FALSE, argc, argv); + crm_set_options("!V?$o:QDUCEX:t:Srwlsh:MmBfbRx:pP5N:A:uncd", "command [options] [data]", long_options, + "Provides direct access to the cluster configuration." + "\n\n Allows the configuration, or sections of it, to be queried, modified, replaced and deleted." + "\n\n Where necessary, XML data will be obtained using the -X, -x, or -p options\n"); + + if(argc < 2) { + crm_help('?',LSB_EXIT_EINVAL); + } + + while (1) { + flag = crm_get_option(argc, argv, &option_index); + if (flag == -1) + break; + + switch(flag) { + case 't': + message_timeout_ms = atoi(optarg); + if(message_timeout_ms < 1) { + message_timeout_ms = 30; } + break; + case 'A': + obj_type = crm_strdup(optarg); + command_options |= cib_xpath; + break; + case 'u': + cib_action = CIB_OP_UPGRADE; + dangerous_cmd = TRUE; + break; + case 'E': + cib_action = CIB_OP_ERASE; + dangerous_cmd = TRUE; + break; + case 'Q': + cib_action = CIB_OP_QUERY; + break; + case 'P': + cib_action = CIB_OP_APPLY_DIFF; + break; + case 'S': + cib_action = CIB_OP_SYNC; + break; + case 'U': + case 'M': + cib_action = CIB_OP_MODIFY; + break; + case 'R': + cib_action = CIB_OP_REPLACE; + break; + case 'C': + cib_action = CIB_OP_CREATE; + break; + case 'D': + cib_action = CIB_OP_DELETE; + break; + case '5': + cib_action = "md5-sum"; + break; + case 'c': + command_options |= cib_can_create; + break; + case 'n': + command_options |= cib_no_children; + break; + case 'm': + cib_action = CIB_OP_ISMASTER; + command_options |= cib_scope_local; + break; + case 'B': + cib_action = CIB_OP_BUMP; + break; + case 'r': + dangerous_cmd = TRUE; + cib_action = CIB_OP_SLAVE; + break; + case 'w': + dangerous_cmd = TRUE; + cib_action = CIB_OP_MASTER; + command_options |= cib_scope_local; + break; + case 'V': + command_options = command_options | cib_verbose; + cl_log_enable_stderr(TRUE); + alter_debug(DEBUG_INC); + break; + case '?': + case '$': + case '!': + crm_help(flag, LSB_EXIT_OK); + break; + case 'o': + crm_debug_2("Option %c => %s", flag, optarg); + obj_type = crm_strdup(optarg); + break; + case 'X': + crm_debug_2("Option %c => %s", flag, optarg); + admin_input_xml = crm_strdup(optarg); + break; + case 'x': + crm_debug_2("Option %c => %s", flag, optarg); + admin_input_file = crm_strdup(optarg); + break; + case 'p': + admin_input_stdin = TRUE; + break; + case 'h': + host = crm_strdup(optarg); + break; + case 'l': + command_options |= cib_scope_local; + break; + case 'd': + cib_action = CIB_OP_DELETE; + command_options |= cib_multiple; + dangerous_cmd = TRUE; + break; + case 'b': + dangerous_cmd = TRUE; + command_options |= cib_inhibit_bcast; + command_options |= cib_scope_local; + break; + case 's': + command_options |= cib_sync_call; + break; + case 'f': + force_flag = TRUE; + command_options |= cib_quorum_override; + break; + default: + printf("Argument code 0%o (%c)" + " is not (?yet?) supported\n", + flag, flag); + ++argerr; + break; } - - if (optind < argc) { - printf("non-option ARGV-elements: "); - while (optind < argc) - printf("%s ", argv[optind++]); - printf("\n"); - crm_help('?', LSB_EXIT_EINVAL); - } - - if (optind > argc || cib_action == NULL) { - ++argerr; - } + } + + if (optind < argc) { + printf("non-option ARGV-elements: "); + while (optind < argc) + printf("%s ", argv[optind++]); + printf("\n"); + crm_help('?', LSB_EXIT_EINVAL); + } + + if (optind > argc || cib_action == NULL) { + ++argerr; + } - if (argerr) { - crm_help('?', LSB_EXIT_GENERIC); - } - - if(dangerous_cmd && force_flag == FALSE) { - fprintf(stderr, "The supplied command is considered dangerous." - " To prevent accidental destruction of the cluster," - " the --force flag is required in order to proceed.\n"); - fflush(stderr); - exit(LSB_EXIT_GENERIC); - } + if (argerr) { + crm_help('?', LSB_EXIT_GENERIC); + } + + if(dangerous_cmd && force_flag == FALSE) { + fprintf(stderr, "The supplied command is considered dangerous." + " To prevent accidental destruction of the cluster," + " the --force flag is required in order to proceed.\n"); + fflush(stderr); + exit(LSB_EXIT_GENERIC); + } - if(admin_input_file != NULL) { - input = filename2xml(admin_input_file); - source = admin_input_file; + if(admin_input_file != NULL) { + input = filename2xml(admin_input_file); + source = admin_input_file; - } else if(admin_input_xml != NULL) { - source = "input string"; - input = string2xml(admin_input_xml); - - } else if(admin_input_stdin) { - source = "STDIN"; - input = stdin2xml(); - } + } else if(admin_input_xml != NULL) { + source = "input string"; + input = string2xml(admin_input_xml); + + } else if(admin_input_stdin) { + source = "STDIN"; + input = stdin2xml(); + } - if(input != NULL) { - crm_log_xml_debug(input, "[admin input]"); - - } else if(source) { - fprintf(stderr, "Couldn't parse input from %s.\n", source); - return 1; + if(input != NULL) { + crm_log_xml_debug(input, "[admin input]"); + + } else if(source) { + fprintf(stderr, "Couldn't parse input from %s.\n", source); + return 1; + } + + if(safe_str_eq(cib_action, "md5-sum")) { + char *digest = NULL; + if(input == NULL) { + fprintf(stderr, + "Please supply XML to process with -X, -x or -p\n"); + exit(1); } - - if(safe_str_eq(cib_action, "md5-sum")) { - char *digest = NULL; - if(input == NULL) { - fprintf(stderr, - "Please supply XML to process with -X, -x or -p\n"); - exit(1); - } - digest = calculate_on_disk_digest(input); - fprintf(stderr, "Digest: "); - fprintf(stdout, "%s\n", crm_str(digest)); - crm_free(digest); - exit(0); - } + digest = calculate_on_disk_digest(input); + fprintf(stderr, "Digest: "); + fprintf(stdout, "%s\n", crm_str(digest)); + crm_free(digest); + exit(0); + } - exit_code = do_init(); - if(exit_code != cib_ok) { - crm_err("Init failed, could not perform requested operations"); - fprintf(stderr, "Init failed, could not perform requested operations\n"); - return -exit_code; - } - - exit_code = do_work(input, command_options, &output); - if (exit_code > 0) { - /* wait for the reply by creating a mainloop and running it until - * the callbacks are invoked... - */ - request_id = exit_code; - - the_cib->cmds->register_callback( - the_cib, request_id, message_timeout_ms, FALSE, NULL, - "cibadmin_op_callback", cibadmin_op_callback); - - mainloop = g_main_new(FALSE); - - crm_debug_3("%s waiting for reply from the local CIB", - crm_system_name); + exit_code = do_init(); + if(exit_code != cib_ok) { + crm_err("Init failed, could not perform requested operations"); + fprintf(stderr, "Init failed, could not perform requested operations\n"); + return -exit_code; + } + + exit_code = do_work(input, command_options, &output); + if (exit_code > 0) { + /* wait for the reply by creating a mainloop and running it until + * the callbacks are invoked... + */ + request_id = exit_code; + + the_cib->cmds->register_callback( + the_cib, request_id, message_timeout_ms, FALSE, NULL, + "cibadmin_op_callback", cibadmin_op_callback); + + mainloop = g_main_new(FALSE); + + crm_debug_3("%s waiting for reply from the local CIB", + crm_system_name); - crm_info("Starting mainloop"); - g_main_run(mainloop); + crm_info("Starting mainloop"); + g_main_run(mainloop); - } else if(exit_code < 0) { - crm_err("Call failed: %s", cib_error2string(exit_code)); - fprintf(stderr, "Call failed: %s\n", - cib_error2string(exit_code)); - operation_status = exit_code; - - if(exit_code == cib_dtd_validation) { - if(crm_str_eq(cib_action, CIB_OP_UPGRADE, TRUE)) { - xmlNode *obj = NULL; - int version = 0, rc = 0; - rc = the_cib->cmds->query(the_cib, NULL, &obj, command_options); - if(rc == cib_ok) { - update_validation(&obj, &version, TRUE, FALSE); - } - - } else if(output) { - validate_xml_verbose(output); - } + } else if(exit_code < 0) { + crm_err("Call failed: %s", cib_error2string(exit_code)); + fprintf(stderr, "Call failed: %s\n", + cib_error2string(exit_code)); + operation_status = exit_code; + + if(exit_code == cib_dtd_validation) { + if(crm_str_eq(cib_action, CIB_OP_UPGRADE, TRUE)) { + xmlNode *obj = NULL; + int version = 0, rc = 0; + rc = the_cib->cmds->query(the_cib, NULL, &obj, command_options); + if(rc == cib_ok) { + update_validation(&obj, &version, TRUE, FALSE); } - } - if(output != NULL) { - char *buffer = dump_xml_formatted(output); - fprintf(stdout, "%s\n", crm_str(buffer)); - crm_free(buffer); - free_xml(output); + } else if(output) { + validate_xml_verbose(output); + } } + } - crm_debug_3("%s exiting normally", crm_system_name); - - free_xml(input); - crm_free(admin_input_xml); - crm_free(admin_input_file); - the_cib->cmds->signoff(the_cib); - cib_delete(the_cib); - crm_xml_cleanup(); + if(output != NULL) { + char *buffer = dump_xml_formatted(output); + fprintf(stdout, "%s\n", crm_str(buffer)); + crm_free(buffer); + free_xml(output); + } - return -exit_code; + crm_debug_3("%s exiting normally", crm_system_name); + + free_xml(input); + crm_free(admin_input_xml); + crm_free(admin_input_file); + the_cib->cmds->signoff(the_cib); + cib_delete(the_cib); + crm_xml_cleanup(); + + return -exit_code; } int do_work(xmlNode *input, int call_options, xmlNode **output) { - /* construct the request */ - the_cib->call_timeout = message_timeout_ms; - if (strcasecmp(CIB_OP_REPLACE, cib_action) == 0 - && safe_str_eq(crm_element_name(input), XML_TAG_CIB)) { - xmlNode *status = get_object_root(XML_CIB_TAG_STATUS, input); - if(status == NULL) { - create_xml_node(input, XML_CIB_TAG_STATUS); - } + /* construct the request */ + the_cib->call_timeout = message_timeout_ms; + if (strcasecmp(CIB_OP_REPLACE, cib_action) == 0 + && safe_str_eq(crm_element_name(input), XML_TAG_CIB)) { + xmlNode *status = get_object_root(XML_CIB_TAG_STATUS, input); + if(status == NULL) { + create_xml_node(input, XML_CIB_TAG_STATUS); } + } - if (strcasecmp(CIB_OP_SYNC, cib_action) == 0) { - crm_debug_4("Performing %s op...", cib_action); - return the_cib->cmds->sync_from( - the_cib, host, obj_type, call_options); - - } else if (strcasecmp(CIB_OP_SLAVE, cib_action) == 0 - && (call_options ^ cib_scope_local) ) { - crm_debug_4("Performing %s op on all nodes...", cib_action); - return the_cib->cmds->set_slave_all(the_cib, call_options); - - } else if (strcasecmp(CIB_OP_MASTER, cib_action) == 0) { - crm_debug_4("Performing %s op on all nodes...", cib_action); - return the_cib->cmds->set_master(the_cib, call_options); - - - } else if(cib_action != NULL) { - crm_debug_4("Passing \"%s\" to variant_op...", cib_action); - return the_cib->cmds->variant_op( - the_cib, cib_action, host, obj_type, - input, output, call_options); + if (strcasecmp(CIB_OP_SYNC, cib_action) == 0) { + crm_debug_4("Performing %s op...", cib_action); + return the_cib->cmds->sync_from( + the_cib, host, obj_type, call_options); + + } else if (strcasecmp(CIB_OP_SLAVE, cib_action) == 0 + && (call_options ^ cib_scope_local) ) { + crm_debug_4("Performing %s op on all nodes...", cib_action); + return the_cib->cmds->set_slave_all(the_cib, call_options); + + } else if (strcasecmp(CIB_OP_MASTER, cib_action) == 0) { + crm_debug_4("Performing %s op on all nodes...", cib_action); + return the_cib->cmds->set_master(the_cib, call_options); + + + } else if(cib_action != NULL) { + crm_debug_4("Passing \"%s\" to variant_op...", cib_action); + return the_cib->cmds->variant_op( + the_cib, cib_action, host, obj_type, + input, output, call_options); - } else { - crm_err("You must specify an operation"); - } - return cib_operation; + } else { + crm_err("You must specify an operation"); + } + return cib_operation; } enum cib_errors do_init(void) { - enum cib_errors rc = cib_ok; - - the_cib = cib_new(); - rc = the_cib->cmds->signon(the_cib, crm_system_name, cib_command); - if(rc != cib_ok) { - crm_err("Signon to CIB failed: %s", - cib_error2string(rc)); - fprintf(stderr, "Signon to CIB failed: %s\n", - cib_error2string(rc)); - } + enum cib_errors rc = cib_ok; + + the_cib = cib_new(); + rc = the_cib->cmds->signon(the_cib, crm_system_name, cib_command); + if(rc != cib_ok) { + crm_err("Signon to CIB failed: %s", + cib_error2string(rc)); + fprintf(stderr, "Signon to CIB failed: %s\n", + cib_error2string(rc)); + } - return rc; + return rc; } void cib_connection_destroy(gpointer user_data) { - crm_err("Connection to the CIB terminated... exiting"); - g_main_quit(mainloop); - return; + crm_err("Connection to the CIB terminated... exiting"); + g_main_quit(mainloop); + return; } void cibadmin_op_callback(xmlNode *msg, int call_id, int rc, xmlNode *output, void *user_data) { - char *admin_input_xml = NULL; + char *admin_input_xml = NULL; - exit_code = rc; + exit_code = rc; - if(output != NULL) { - admin_input_xml = dump_xml_formatted(output); - } + if(output != NULL) { + admin_input_xml = dump_xml_formatted(output); + } - if(safe_str_eq(cib_action, CIB_OP_ISMASTER) && rc != cib_ok) { - crm_info("CIB on %s is _not_ the master instance", - host?host:"localhost"); - fprintf(stderr, "CIB on %s is _not_ the master instance\n", - host?host:"localhost"); + if(safe_str_eq(cib_action, CIB_OP_ISMASTER) && rc != cib_ok) { + crm_info("CIB on %s is _not_ the master instance", + host?host:"localhost"); + fprintf(stderr, "CIB on %s is _not_ the master instance\n", + host?host:"localhost"); - } else if(safe_str_eq(cib_action, CIB_OP_ISMASTER)) { - crm_info("CIB on %s _is_ the master instance", - host?host:"localhost"); - fprintf(stderr, "CIB on %s _is_ the master instance\n", - host?host:"localhost"); + } else if(safe_str_eq(cib_action, CIB_OP_ISMASTER)) { + crm_info("CIB on %s _is_ the master instance", + host?host:"localhost"); + fprintf(stderr, "CIB on %s _is_ the master instance\n", + host?host:"localhost"); - } else if(rc != 0) { - crm_warn("Call %s failed (%d): %s", - cib_action, rc, cib_error2string(rc)); - fprintf(stderr, "Call %s failed (%d): %s\n", - cib_action, rc, cib_error2string(rc)); - fprintf(stdout, "%s\n", crm_str(admin_input_xml)); - - } else if(safe_str_eq(cib_action, CIB_OP_QUERY) && output==NULL) { - crm_err("Output expected in query response"); - crm_log_xml(LOG_ERR, "no output", msg); - - } else if(output == NULL) { - crm_info("Call passed"); - - } else { - crm_info("Call passed"); - fprintf(stdout, "%s\n", crm_str(admin_input_xml)); - } - crm_free(admin_input_xml); - - if(call_id == request_id) { - g_main_quit(mainloop); + } else if(rc != 0) { + crm_warn("Call %s failed (%d): %s", + cib_action, rc, cib_error2string(rc)); + fprintf(stderr, "Call %s failed (%d): %s\n", + cib_action, rc, cib_error2string(rc)); + fprintf(stdout, "%s\n", crm_str(admin_input_xml)); + + } else if(safe_str_eq(cib_action, CIB_OP_QUERY) && output==NULL) { + crm_err("Output expected in query response"); + crm_log_xml(LOG_ERR, "no output", msg); + + } else if(output == NULL) { + crm_info("Call passed"); + + } else { + crm_info("Call passed"); + fprintf(stdout, "%s\n", crm_str(admin_input_xml)); + } + crm_free(admin_input_xml); + + if(call_id == request_id) { + g_main_quit(mainloop); - } else { - crm_info("Message was not the response we were looking for (%d vs. %d", call_id, request_id); - } + } else { + crm_info("Message was not the response we were looking for (%d vs. %d", call_id, request_id); + } } diff --git a/tools/crm_inject.c b/tools/crm_inject.c index 46da60d8dd..ee1e67ca0a 100644 --- a/tools/crm_inject.c +++ b/tools/crm_inject.c @@ -1,1176 +1,1177 @@ /* * Copyright (C) 2009 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include cib_t *global_cib = NULL; GListPtr op_fail = NULL; gboolean quiet = FALSE; #define new_node_template "//"XML_CIB_TAG_NODE"[@uname='%s']" #define node_template "//"XML_CIB_TAG_STATE"[@uname='%s']" #define rsc_template "//"XML_CIB_TAG_STATE"[@uname='%s']//"XML_LRM_TAG_RESOURCE"[@id='%s']" #define op_template "//"XML_CIB_TAG_STATE"[@uname='%s']//"XML_LRM_TAG_RESOURCE"[@id='%s']/"XML_LRM_TAG_RSC_OP"[@id='%s']" /* #define op_template "//"XML_CIB_TAG_STATE"[@uname='%s']//"XML_LRM_TAG_RESOURCE"[@id='%s']/"XML_LRM_TAG_RSC_OP"[@id='%s' and @"XML_LRM_ATTR_CALLID"='%d']" */ #define FAKE_TE_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" #define quiet_log(fmt, args...) do { \ if(quiet == FALSE) { \ printf(fmt , ##args); \ } \ } while(0) extern void cleanup_alloc_calculations(pe_working_set_t *data_set); extern xmlNode * do_calculations( pe_working_set_t *data_set, xmlNode *xml_input, ha_time_t *now); char *use_date = NULL; static ha_time_t *get_date(void) { if(use_date) { char *date_m = use_date; return parse_date(&date_m); } return NULL; } static xmlNode *find_resource(xmlNode *cib_node, const char *resource) { char *xpath = NULL; xmlNode *match = NULL; const char *node = crm_element_value(cib_node, XML_ATTR_UNAME); int max = strlen(rsc_template) + strlen(resource) + strlen(node) + 1; crm_malloc0(xpath, max); snprintf(xpath, max, rsc_template, node, resource); match = get_xpath_object(xpath, cib_node, LOG_DEBUG_2); crm_free(xpath); return match; } static void create_node_entry(cib_t *cib_conn, char *node) { int rc = cib_ok; int max = strlen(new_node_template) + strlen(node) + 1; char *xpath = NULL; crm_malloc0(xpath, max); snprintf(xpath, max, new_node_template, node); rc = cib_conn->cmds->query(cib_conn, xpath, NULL, cib_xpath|cib_sync_call|cib_scope_local); if (rc == cib_NOTEXISTS) { xmlNode *cib_object = create_xml_node(NULL, XML_CIB_TAG_NODE); /* Using node uname as uuid ala corosync/openais */ crm_xml_add(cib_object, XML_ATTR_ID, node); crm_xml_add(cib_object, XML_ATTR_UNAME, node); crm_xml_add(cib_object, XML_ATTR_TYPE, NORMALNODE); cib_conn->cmds->create(cib_conn, XML_CIB_TAG_NODES, cib_object, cib_sync_call|cib_scope_local); /* Not bothering with subsequent query to see if it exists, we'll bomb out later in the call to determine_host... */ free_xml(cib_object); } crm_free(xpath); } static xmlNode *inject_node_state(cib_t *cib_conn, char *node) { int rc = cib_ok; int max = strlen(rsc_template) + strlen(node) + 1; char *xpath = NULL; xmlNode *cib_object = NULL; crm_malloc0(xpath, max); create_node_entry(cib_conn, node); snprintf(xpath, max, node_template, node); rc = cib_conn->cmds->query(cib_conn, xpath, &cib_object, cib_xpath|cib_sync_call|cib_scope_local); if(rc == cib_NOTEXISTS) { char *uuid = NULL; cib_object = create_xml_node(NULL, XML_CIB_TAG_STATE); determine_host(cib_conn, &node, &uuid); crm_xml_add(cib_object, XML_ATTR_UUID, uuid); crm_xml_add(cib_object, XML_ATTR_UNAME, node); cib_conn->cmds->create(cib_conn, XML_CIB_TAG_STATUS, cib_object, cib_sync_call|cib_scope_local); free_xml(cib_object); crm_free(uuid); rc = cib_conn->cmds->query(cib_conn, xpath, &cib_object, cib_xpath|cib_sync_call|cib_scope_local); } crm_free(xpath); CRM_ASSERT(rc == cib_ok); return cib_object; } static xmlNode *modify_node(cib_t *cib_conn, char *node, gboolean up) { xmlNode *cib_node = inject_node_state(cib_conn, node); if(up) { crm_xml_add(cib_node, XML_CIB_ATTR_HASTATE, ACTIVESTATUS); crm_xml_add(cib_node, XML_CIB_ATTR_INCCM, XML_BOOLEAN_YES); crm_xml_add(cib_node, XML_CIB_ATTR_CRMDSTATE, ONLINESTATUS); crm_xml_add(cib_node, XML_CIB_ATTR_JOINSTATE, CRMD_JOINSTATE_MEMBER); crm_xml_add(cib_node, XML_CIB_ATTR_EXPSTATE, CRMD_JOINSTATE_MEMBER); } else { crm_xml_add(cib_node, XML_CIB_ATTR_HASTATE, DEADSTATUS); crm_xml_add(cib_node, XML_CIB_ATTR_INCCM, XML_BOOLEAN_NO); crm_xml_add(cib_node, XML_CIB_ATTR_CRMDSTATE, OFFLINESTATUS); crm_xml_add(cib_node, XML_CIB_ATTR_JOINSTATE, CRMD_JOINSTATE_DOWN); crm_xml_add(cib_node, XML_CIB_ATTR_EXPSTATE, CRMD_JOINSTATE_DOWN); } crm_xml_add(cib_node, XML_ATTR_ORIGIN, crm_system_name); return cib_node; } static void inject_transient_attr(xmlNode *cib_node, const char *name, const char *value) { xmlNode *attrs = NULL; xmlNode *container = NULL; xmlNode *nvp = NULL; const char *node_uuid = ID(cib_node); char *nvp_id = crm_concat(name, node_uuid, '-'); crm_info("Injecting attribute %s=%s into %s '%s'", name, value, xmlGetNodePath(cib_node), ID(cib_node)); attrs = first_named_child(cib_node, XML_TAG_TRANSIENT_NODEATTRS); if(attrs == NULL) { attrs = create_xml_node(cib_node, XML_TAG_TRANSIENT_NODEATTRS); crm_xml_add(attrs, XML_ATTR_ID, node_uuid); } container = first_named_child(attrs, XML_TAG_ATTR_SETS); if(container == NULL) { container = create_xml_node(attrs, XML_TAG_ATTR_SETS); crm_xml_add(container, XML_ATTR_ID, node_uuid); } nvp = create_xml_node(container, XML_CIB_TAG_NVPAIR); crm_xml_add(nvp, XML_ATTR_ID, nvp_id); crm_xml_add(nvp, XML_NVPAIR_ATTR_NAME, name); crm_xml_add(nvp, XML_NVPAIR_ATTR_VALUE, value); crm_free(nvp_id); } static xmlNode *inject_resource(xmlNode *cib_node, const char *resource, const char *rclass, const char *rtype, const char *rprovider) { xmlNode *lrm = NULL; xmlNode *container = NULL; xmlNode *cib_resource = NULL; cib_resource = find_resource(cib_node, resource); if(cib_resource != NULL) { return cib_resource; } /* One day, add query for class, provider, type */ if(rclass == NULL || rtype == NULL) { fprintf(stderr, "Resource %s not found in the status section of %s." " Please supply the class and type to continue\n", resource, ID(cib_node)); return NULL; } else if(safe_str_neq(rclass, "ocf") && safe_str_neq(rclass, "stonith") && safe_str_neq(rclass, "heartbeat") && safe_str_neq(rclass, "lsb")) { fprintf(stderr, "Invalid class for %s: %s\n", resource, rclass); return NULL; } else if(safe_str_eq(rclass, "ocf") && rprovider == NULL) { fprintf(stderr, "Please specify the provider for resource %s\n", resource); return NULL; } crm_info("Injecting new resource %s into %s '%s'", resource, xmlGetNodePath(cib_node), ID(cib_node)); lrm = first_named_child(cib_node, XML_CIB_TAG_LRM); if(lrm == NULL) { const char *node_uuid = ID(cib_node); lrm = create_xml_node(cib_node, XML_CIB_TAG_LRM); crm_xml_add(lrm, XML_ATTR_ID, node_uuid); } container = first_named_child(lrm, XML_LRM_TAG_RESOURCES); if(container == NULL) { container = create_xml_node(lrm, XML_LRM_TAG_RESOURCES); } cib_resource = create_xml_node(container, XML_LRM_TAG_RESOURCE); crm_xml_add(cib_resource, XML_ATTR_ID, resource); crm_xml_add(cib_resource, XML_AGENT_ATTR_CLASS, rclass); crm_xml_add(cib_resource, XML_AGENT_ATTR_PROVIDER, rprovider); crm_xml_add(cib_resource, XML_ATTR_TYPE, rtype); return cib_resource; } static lrm_op_t *create_op( xmlNode *cib_resource, const char *task, int interval, int outcome) { lrm_op_t *op = NULL; + xmlNode *xop = NULL; crm_malloc0(op, sizeof(lrm_op_t)); op->app_name = crm_strdup(crm_system_name); op->rsc_id = crm_strdup(ID(cib_resource)); op->interval = interval; op->op_type = crm_strdup(task); op->rc = outcome; op->op_status = 0; op->params = NULL; /* TODO: Fill me in */ op->call_id = 0; - xml_child_iter(cib_resource, xop, - int tmp = 0; - crm_element_value_int(xop, XML_LRM_ATTR_CALLID, &tmp); - if(tmp > op->call_id) { - op->call_id = tmp; - } - ); + for(xop = cib_resource; xop != NULL; xop = xop->next) { + int tmp = 0; + crm_element_value_int(xop, XML_LRM_ATTR_CALLID, &tmp); + if(tmp > op->call_id) { + op->call_id = tmp; + } + } op->call_id++; return op; } static xmlNode *inject_op(xmlNode *cib_resource, lrm_op_t *op, int target_rc) { return create_operation_update(cib_resource, op, CRM_FEATURE_SET, target_rc, crm_system_name, LOG_DEBUG_2); } static void update_failcounts(xmlNode *cib_node, const char *resource, int interval, int rc) { if(rc == 0) { return; } else if(rc == 7 && interval == 0) { return; } else { char *name = NULL; char *now = crm_itoa(time(NULL)); name = crm_concat("fail-count", resource, '-'); inject_transient_attr(cib_node, name, "value++"); name = crm_concat("last-failure", resource, '-'); inject_transient_attr(cib_node, name, now); crm_free(name); crm_free(now); } } static gboolean exec_pseudo_action(crm_graph_t *graph, crm_action_t *action) { action->confirmed = TRUE; update_graph(graph, action); return TRUE; } static gboolean exec_rsc_action(crm_graph_t *graph, crm_action_t *action) { int rc = 0; GListPtr gIter = NULL; lrm_op_t *op = NULL; int target_outcome = 0; const char *rtype = NULL; const char *rclass = NULL; const char *resource = NULL; const char *rprovider = NULL; const char *target_rc_s = crm_meta_value(action->params, XML_ATTR_TE_TARGET_RC); xmlNode *cib_node = NULL; xmlNode *cib_resource = NULL; xmlNode *action_rsc = first_named_child(action->xml, XML_CIB_TAG_RESOURCE); char *node = crm_element_value_copy(action->xml, XML_LRM_ATTR_TARGET); if(safe_str_eq(crm_element_value(action->xml, "operation"), "probe_complete")) { crm_info("Skipping %s op for %s\n", crm_element_value(action->xml, "operation"), node); goto done; } if(action_rsc == NULL) { crm_log_xml_err(action->xml, "Bad"); crm_free(node); return FALSE; } resource = ID(action_rsc); rclass = crm_element_value(action_rsc, XML_AGENT_ATTR_CLASS); rtype = crm_element_value(action_rsc, XML_ATTR_TYPE); rprovider = crm_element_value(action_rsc, XML_AGENT_ATTR_PROVIDER); if(target_rc_s != NULL) { target_outcome = crm_parse_int(target_rc_s, "0"); } CRM_ASSERT(global_cib->cmds->query(global_cib, NULL, NULL, cib_sync_call|cib_scope_local) == cib_ok); cib_node = inject_node_state(global_cib, node); CRM_ASSERT(cib_node != NULL); cib_resource = inject_resource(cib_node, resource, rclass, rtype, rprovider); CRM_ASSERT(cib_resource != NULL); op = convert_graph_action(cib_resource, action, 0, target_outcome); quiet_log(" * Executing action %d: %s_%s_%d on %s\n", action->id, resource, op->op_type, op->interval, node); for(gIter = op_fail; gIter != NULL; gIter = gIter->next) { char *spec = (char*)gIter->data; char *key = NULL; crm_malloc0(key, strlen(spec)); snprintf(key, strlen(spec), "%s_%s_%d@%s=", resource, op->op_type, op->interval, node); if(strncasecmp(key, spec, strlen(key)) == 0) { rc = sscanf(spec, "%*[^=]=%d", &op->rc); action->failed = TRUE; graph->abort_priority = INFINITY; printf("\tPretending action %d failed with rc=%d\n", action->id, op->rc); update_failcounts(cib_node, resource, op->interval, op->rc); break; } } inject_op(cib_resource, op, target_outcome); free_lrm_op(op); rc = global_cib->cmds->modify(global_cib, XML_CIB_TAG_STATUS, cib_node, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == cib_ok); done: crm_free(node); free_xml(cib_node); action->confirmed = TRUE; update_graph(graph, action); return TRUE; } static gboolean exec_crmd_action(crm_graph_t *graph, crm_action_t *action) { action->confirmed = TRUE; update_graph(graph, action); return TRUE; } #define STATUS_PATH_MAX 512 static gboolean exec_stonith_action(crm_graph_t *graph, crm_action_t *action) { int rc = 0; char xpath[STATUS_PATH_MAX]; char *target = crm_element_value_copy(action->xml, XML_LRM_ATTR_TARGET); xmlNode *cib_node = modify_node(global_cib, target, FALSE); crm_xml_add(cib_node, XML_ATTR_ORIGIN, __FUNCTION__); CRM_ASSERT(cib_node != NULL); quiet_log(" * Fencing %s\n", target); rc = global_cib->cmds->replace(global_cib, XML_CIB_TAG_STATUS, cib_node, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == cib_ok); snprintf(xpath, STATUS_PATH_MAX, "//node_state[@uname='%s']/%s", target, XML_CIB_TAG_LRM); rc = global_cib->cmds->delete(global_cib, xpath, NULL, cib_xpath|cib_sync_call|cib_scope_local); snprintf(xpath, STATUS_PATH_MAX, "//node_state[@uname='%s']/%s", target, XML_TAG_TRANSIENT_NODEATTRS); rc = global_cib->cmds->delete(global_cib, xpath, NULL, cib_xpath|cib_sync_call|cib_scope_local); action->confirmed = TRUE; update_graph(graph, action); free_xml(cib_node); crm_free(target); return TRUE; } static char * add_list_element(char *list, const char *value) { int len = 0; int last = 0; if(value == NULL) { return list; } if(list) { last = strlen(list); } len = last + 2; /* +1 space, +1 EOS */ len += strlen(value); crm_realloc(list, len); sprintf(list + last, " %s", value); return list; } static void print_cluster_status(pe_working_set_t *data_set) { char *online_nodes = NULL; char *offline_nodes = NULL; GListPtr gIter = NULL; for(gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t*)gIter->data; const char *node_mode = NULL; if(node->details->unclean) { if(node->details->online && node->details->unclean) { node_mode = "UNCLEAN (online)"; } else if(node->details->pending) { node_mode = "UNCLEAN (pending)"; } else { node_mode = "UNCLEAN (offline)"; } } else if(node->details->pending) { node_mode = "pending"; } else if(node->details->standby_onfail && node->details->online) { node_mode = "standby (on-fail)"; } else if(node->details->standby) { if(node->details->online) { node_mode = "standby"; } else { node_mode = "OFFLINE (standby)"; } } else if(node->details->online) { node_mode = "online"; online_nodes = add_list_element(online_nodes, node->details->uname); continue; } else { node_mode = "OFFLINE"; offline_nodes = add_list_element(offline_nodes, node->details->uname); continue; } if(safe_str_eq(node->details->uname, node->details->id)) { printf("Node %s: %s\n", node->details->uname, node_mode); } else { printf("Node %s (%s): %s\n", node->details->uname, node->details->id, node_mode); } } if(online_nodes) { printf("Online: [%s ]\n", online_nodes); crm_free(online_nodes); } if(offline_nodes) { printf("OFFLINE: [%s ]\n", offline_nodes); crm_free(offline_nodes); } fprintf(stdout, "\n"); for(gIter = data_set->resources; gIter != NULL; gIter = gIter->next) { resource_t *rsc = (resource_t*)gIter->data; if(is_set(rsc->flags, pe_rsc_orphan) && rsc->role == RSC_ROLE_STOPPED) { continue; } rsc->fns->print(rsc, NULL, pe_print_printf, stdout); } fprintf(stdout, "\n"); } static int run_simulation(pe_working_set_t *data_set) { crm_graph_t *transition = NULL; enum transition_status graph_rc = -1; crm_graph_functions_t exec_fns = { exec_pseudo_action, exec_rsc_action, exec_crmd_action, exec_stonith_action, }; set_graph_functions(&exec_fns); quiet_log("\nExecuting cluster transition:\n"); transition = unpack_graph(data_set->graph, crm_system_name); print_graph(LOG_DEBUG, transition); do { graph_rc = run_graph(transition); } while(graph_rc == transition_active); if(graph_rc != transition_complete) { fprintf(stdout, "Transition failed: %s\n", transition_status(graph_rc)); print_graph(LOG_ERR, transition); } destroy_graph(transition); if(graph_rc != transition_complete) { fprintf(stdout, "An invalid transition was produced\n"); } if(quiet == FALSE) { xmlNode *cib_object = NULL; int rc = global_cib->cmds->query(global_cib, NULL, &cib_object, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == cib_ok); quiet_log("\nRevised cluster status:\n"); cleanup_alloc_calculations(data_set); data_set->input = cib_object; data_set->now = get_date(); cluster_status(data_set); print_cluster_status(data_set); } if(graph_rc != transition_complete) { return graph_rc; } return 0; } 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(is_set(action->flags, pe_action_pseudo)) { action_name = crm_strdup(action->uuid); } else { action_host = ""; action_name = crm_concat(action->uuid, action_host, ' '); } 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; } static void create_dotfile(pe_working_set_t *data_set, const char *dot_file, gboolean all_actions) { GListPtr gIter = NULL; FILE *dot_strm = fopen(dot_file, "w"); if(dot_strm == NULL) { crm_perror(LOG_ERR,"Could not open %s for writing", dot_file); return; } fprintf(dot_strm, " digraph \"g\" {\n"); for(gIter = data_set->actions; gIter != NULL; gIter = gIter->next) { action_t *action = (action_t*)gIter->data; 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(is_set(action->flags, pe_action_pseudo)) { font = "orange"; } style = "dashed"; if(is_set(action->flags, pe_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(is_set(action->flags, pe_action_optional)) { color = "blue"; if(all_actions == FALSE) { goto dont_write; } } else { color = "red"; CRM_CHECK(is_set(action->flags, pe_action_runnable) == FALSE, ;); } set_bit_inplace(action->flags, pe_action_dumped); fprintf(dot_strm, "\"%s\" [ style=%s color=\"%s\" fontcolor=\"%s\" %s%s]\n", action_name, style, color, font, fill?"fillcolor=":"", fill?fill:""); dont_write: crm_free(action_name); } for(gIter = data_set->actions; gIter != NULL; gIter = gIter->next) { action_t *action = (action_t*)gIter->data; GListPtr gIter2 = NULL; for(gIter2 = action->actions_before; gIter2 != NULL; gIter2 = gIter2->next) { action_wrapper_t *before = (action_wrapper_t*)gIter2->data; char *before_name = NULL; char *after_name = NULL; const char *style = "dashed"; gboolean optional = TRUE; if(before->state == pe_link_dumped) { optional = FALSE; style = "bold"; } else if(is_set(action->flags, pe_action_pseudo) && (before->type & pe_order_stonith_stop)) { continue; } else if(before->state == pe_link_dup) { continue; } else if(before->type == pe_order_none) { continue; } else if(is_set(before->action->flags, pe_action_dumped) && is_set(action->flags, pe_action_dumped)) { optional = FALSE; } if(all_actions || optional == FALSE) { before_name = create_action_name(before->action); after_name = create_action_name(action); fprintf(dot_strm, "\"%s\" -> \"%s\" [ style = %s]\n", before_name, after_name, style); crm_free(before_name); crm_free(after_name); } } } fprintf(dot_strm, "}\n"); if(dot_strm != NULL) { fflush(dot_strm); fclose(dot_strm); } } static void modify_configuration( pe_working_set_t *data_set, const char *quorum, GListPtr node_up, GListPtr node_down, GListPtr node_fail, GListPtr op_inject) { int rc = cib_ok; GListPtr gIter = NULL; xmlNode *cib_op = NULL; xmlNode *cib_node = NULL; xmlNode *cib_resource = NULL; lrm_op_t *op = NULL; if(quorum) { xmlNode *top = create_xml_node(NULL, XML_TAG_CIB); quiet_log(" + Setting quorum: %s\n", quorum); /* crm_xml_add(top, XML_ATTR_DC_UUID, dc_uuid); */ crm_xml_add(top, XML_ATTR_HAVE_QUORUM, quorum); rc = global_cib->cmds->modify(global_cib, NULL, top, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == cib_ok); } for(gIter = node_up; gIter != NULL; gIter = gIter->next) { char *node = (char*)gIter->data; quiet_log(" + Bringing node %s online\n", node); cib_node = modify_node(global_cib, node, TRUE); CRM_ASSERT(cib_node != NULL); rc = global_cib->cmds->modify(global_cib, XML_CIB_TAG_STATUS, cib_node, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == cib_ok); } for(gIter = node_down; gIter != NULL; gIter = gIter->next) { char *node = (char*)gIter->data; quiet_log(" + Taking node %s offline\n", node); cib_node = modify_node(global_cib, node, FALSE); CRM_ASSERT(cib_node != NULL); rc = global_cib->cmds->modify(global_cib, XML_CIB_TAG_STATUS, cib_node, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == cib_ok); } for(gIter = node_fail; gIter != NULL; gIter = gIter->next) { char *node = (char*)gIter->data; quiet_log(" + Failing node %s\n", node); cib_node = modify_node(global_cib, node, TRUE); crm_xml_add(cib_node, XML_CIB_ATTR_INCCM, XML_BOOLEAN_NO); CRM_ASSERT(cib_node != NULL); rc = global_cib->cmds->modify(global_cib, XML_CIB_TAG_STATUS, cib_node, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == cib_ok); } for(gIter = op_inject; gIter != NULL; gIter = gIter->next) { char *spec = (char*)gIter->data; int rc = 0; int outcome = 0; int interval = 0; char *key = NULL; char *node = NULL; char *task = NULL; char *resource = NULL; const char *rtype = NULL; const char *rclass = NULL; const char *rprovider = NULL; resource_t *rsc = NULL; quiet_log(" + Injecting %s into the configuration\n", spec); crm_malloc0(key, strlen(spec)+1); crm_malloc0(node, strlen(spec)+1); rc = sscanf(spec, "%[^@]@%[^=]=%d", key, node, &outcome); CRM_CHECK(rc == 3, fprintf(stderr, "Invalid operation spec: %s. Only found %d fields\n", spec, rc); continue); parse_op_key(key, &resource, &task, &interval); crm_free(task); rsc = pe_find_resource(data_set->resources, resource); CRM_CHECK(rsc != NULL, fprintf(stderr, "Invalid resource name: %s\n", resource); continue); rclass = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); rtype = crm_element_value(rsc->xml, XML_ATTR_TYPE); rprovider = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER); cib_node = inject_node_state(global_cib, node); CRM_ASSERT(cib_node != NULL); update_failcounts(cib_node, resource, interval, rc); cib_resource = inject_resource(cib_node, resource, rclass, rtype, rprovider); CRM_ASSERT(cib_resource != NULL); op = create_op(cib_resource, task, interval, outcome); CRM_ASSERT(op != NULL); cib_op = inject_op(cib_resource, op, 0); CRM_ASSERT(cib_op != NULL); free_lrm_op(op); rc = global_cib->cmds->modify(global_cib, XML_CIB_TAG_STATUS, cib_node, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == cib_ok); } } static void setup_input(const char *input, const char *output) { int rc = cib_ok; cib_t *cib_conn = NULL; xmlNode *cib_object = NULL; char *local_output = NULL; if(input == NULL) { /* Use live CIB */ cib_conn = cib_new(); rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command); if(rc == cib_ok) { cib_object = get_cib_copy(cib_conn); } cib_conn->cmds->signoff(cib_conn); cib_delete(cib_conn); cib_conn = NULL; if(cib_object == NULL) { fprintf(stderr, "Live CIB query failed: empty result\n"); exit(3); } } else if(safe_str_eq(input, "-")) { cib_object = filename2xml(NULL); } else { cib_object = filename2xml(input); } if(get_object_root(XML_CIB_TAG_STATUS, cib_object) == NULL) { create_xml_node(cib_object, XML_CIB_TAG_STATUS); } if(cli_config_update(&cib_object, NULL, FALSE) == FALSE) { free_xml(cib_object); exit(cib_STALE); } if(validate_xml(cib_object, NULL, FALSE) != TRUE) { free_xml(cib_object); exit(cib_dtd_validation); } if(output == NULL) { char *pid = crm_itoa(getpid()); local_output = get_shadow_file(pid); output = local_output; crm_free(pid); } rc = write_xml_file(cib_object, output, FALSE); free_xml(cib_object); cib_object = NULL; if(rc < 0) { fprintf(stderr, "Could not create '%s': %s\n", output, strerror(errno)); exit(rc); } setenv("CIB_file", output, 1); crm_free(local_output); } static struct crm_option long_options[] = { /* Top-level Options */ {"help", 0, 0, '?', "\tThis text"}, {"version", 0, 0, '$', "\tVersion information" }, {"quiet", 0, 0, 'Q', "\tDisplay only essentialoutput"}, {"verbose", 0, 0, 'V', "\tIncrease debug output"}, {"-spacer-", 0, 0, '-', "\nOperations:"}, {"run", 0, 0, 'R', "\tDetermine the cluster's response to the given configuration and status"}, {"simulate", 0, 0, 'S', "Simulate the transition's execution and display the resulting cluster status"}, {"in-place", 0, 0, 'X', "Simulate the transition's execution and store the result back to the input file"}, {"show-scores", 0, 0, 's', "Show allocation scores"}, {"show-utilization", 0, 0, 'U', "Show utilization information"}, {"-spacer-", 0, 0, '-', "\nSynthetic Cluster Events:"}, {"node-up", 1, 0, 'u', "\tBring a node online"}, {"node-down", 1, 0, 'd', "\tTake a node offline"}, {"node-fail", 1, 0, 'f', "\tMark a node as failed"}, {"op-inject", 1, 0, 'i', "\t$rsc_$task_$interval@$node=$rc - Inject the specified task before running the simulation"}, {"op-fail", 1, 0, 'F', "\t$rsc_$task_$interval@$node=$rc - Fail the specified task while running the simulation"}, {"set-datetime", 1, 0, 't', "Set date/time"}, {"quorum", 1, 0, 'q', "\tSpecify a value for quorum"}, {"-spacer-", 0, 0, '-', "\nOutput Options:"}, {"save-input", 1, 0, 'I', "\tSave the input configuration to the named file"}, {"save-output", 1, 0, 'O', "Save the output configuration to the named file"}, {"save-graph", 1, 0, 'G', "\tSave the transition graph (XML format) to the named file"}, {"save-dotfile", 1, 0, 'D', "Save the transition graph (DOT format) to the named file"}, {"all-actions", 0, 0, 'a', "\tDisplay all possible actions in the DOT graph - even ones not part of the transition"}, {"-spacer-", 0, 0, '-', "\nData Source:"}, {"live-check", 0, 0, 'L', "\tConnect to the CIB and use the current contents as input"}, {"xml-file", 1, 0, 'x', "\tRetrieve XML from the named file"}, {"xml-pipe", 0, 0, 'p', "\tRetrieve XML from stdin"}, {0, 0, 0, 0} }; int main(int argc, char ** argv) { int rc = 0; guint modified = 0; gboolean store = FALSE; gboolean process = FALSE; gboolean verbose = FALSE; gboolean simulate = FALSE; gboolean all_actions = FALSE; pe_working_set_t data_set; const char *xml_file = "-"; const char *quorum = NULL; const char *dot_file = NULL; const char *graph_file = NULL; const char *input_file = NULL; const char *output_file = NULL; int flag = 0; int index = 0; int argerr = 0; GListPtr node_up = NULL; GListPtr node_down = NULL; GListPtr node_fail = NULL; GListPtr op_inject = NULL; xmlNode *input = NULL; crm_log_init(NULL, LOG_ERR, FALSE, FALSE, argc, argv); crm_set_options("?$VQx:Lpu:d:f:i:RSXD:G:I:O:sUaF:t:q:", "datasource operation [additional options]", long_options, "Tool for simulating the cluster's response to events"); if(argc < 2) { crm_help('?', LSB_EXIT_EINVAL); } while (1) { flag = crm_get_option(argc, argv, &index); if (flag == -1) break; switch(flag) { case 'V': verbose = TRUE; alter_debug(DEBUG_INC); cl_log_enable_stderr(TRUE); break; case '?': case '$': crm_help(flag, LSB_EXIT_OK); break; case 'p': xml_file = "-"; break; case 'Q': quiet = TRUE; break; case 'L': xml_file = NULL; break; case 'x': xml_file = optarg; break; case 'u': modified++; node_up = g_list_append(node_up, optarg); break; case 'd': modified++; node_down = g_list_append(node_down, optarg); break; case 'f': modified++; node_fail = g_list_append(node_fail, optarg); break; case 't': use_date = crm_strdup(optarg); break; case 'i': modified++; op_inject = g_list_append(op_inject, optarg); break; case 'F': process = TRUE; simulate = TRUE; op_fail = g_list_append(op_fail, optarg); break; case 'q': modified++; quorum = optarg; break; case 'a': all_actions = TRUE; break; case 's': process = TRUE; show_scores = TRUE; break; case 'U': process = TRUE; show_utilization = TRUE; break; case 'S': process = TRUE; simulate = TRUE; break; case 'X': store = TRUE; process = TRUE; simulate = TRUE; break; case 'R': process = TRUE; break; case 'D': process = TRUE; dot_file = optarg; break; case 'G': process = TRUE; graph_file = optarg; break; case 'I': input_file = optarg; break; case 'O': simulate = TRUE; output_file = optarg; break; default: ++argerr; break; } } if (optind > argc) { ++argerr; } if (argerr) { crm_help('?', LSB_EXIT_GENERIC); } update_all_trace_data(); /* again, so we see which trace points got updated */ setup_input(xml_file, store?xml_file:output_file); global_cib = cib_new(); global_cib->cmds->signon(global_cib, crm_system_name, cib_command); set_working_set_defaults(&data_set); if(data_set.now != NULL) { quiet_log(" + Setting effective cluster time: %s", use_date); log_date(LOG_WARNING, "Set fake 'now' to", data_set.now, ha_log_date|ha_log_time); } rc = global_cib->cmds->query(global_cib, NULL, &input, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == cib_ok); data_set.input = input; data_set.now = get_date(); cluster_status(&data_set); if(quiet == FALSE) { quiet_log("\nCurrent cluster status:\n"); print_cluster_status(&data_set); } if(modified) { quiet_log("Performing requested modifications\n"); modify_configuration(&data_set, quorum, node_up, node_down, node_fail, op_inject); rc = global_cib->cmds->query(global_cib, NULL, &input, cib_sync_call); if(rc != cib_ok) { fprintf(stderr, "Could not connect to the CIB for input: %s\n", cib_error2string(rc)); goto done; } cleanup_alloc_calculations(&data_set); data_set.now = get_date(); data_set.input = input; } if(input_file != NULL) { rc = write_xml_file(input, input_file, FALSE); if(rc < 0) { fprintf(stderr, "Could not create '%s': %s\n", input_file, strerror(errno)); goto done; } } rc = 0; if(process || simulate) { ha_time_t *local_date = NULL; if(show_scores && show_utilization) { printf("Allocation scores and utilization information:\n"); } else if(show_scores) { fprintf(stdout, "Allocation scores:\n"); } else if(show_utilization) { printf("Utilization information:\n"); } do_calculations(&data_set, input, local_date); input = NULL; /* Don't try and free it twice */ if(graph_file != NULL) { char *msg_buffer = dump_xml_formatted(data_set.graph); FILE *graph_strm = fopen(graph_file, "w"); if(graph_strm == NULL) { crm_perror(LOG_ERR,"Could not open %s for writing", graph_file); } else { if(fprintf(graph_strm, "%s\n\n", msg_buffer) < 0) { crm_perror(LOG_ERR,"Write to %s failed", graph_file); } fflush(graph_strm); fclose(graph_strm); } crm_free(msg_buffer); } if(dot_file != NULL) { create_dotfile(&data_set, dot_file, all_actions); } if(quiet == FALSE && verbose == FALSE) { GListPtr gIter = NULL; quiet_log("%sTransition Summary:\n", show_scores||show_utilization||modified?"\n":""); fflush(stdout); crm_log_level = LOG_NOTICE; cl_log_enable_stderr(TRUE); for(gIter = data_set.resources; gIter != NULL; gIter = gIter->next) { resource_t *rsc = (resource_t*)gIter->data; LogActions(rsc, &data_set); } cl_log_enable_stderr(FALSE); } } if(simulate) { rc = run_simulation(&data_set); } done: cleanup_alloc_calculations(&data_set); global_cib->cmds->signoff(global_cib); cib_delete(global_cib); crm_free(use_date); crm_xml_cleanup(); fflush(stderr); return rc; } diff --git a/tools/crm_mon.c b/tools/crm_mon.c index 4698a8d7a8..4190f4639a 100644 --- a/tools/crm_mon.c +++ b/tools/crm_mon.c @@ -1,1970 +1,1978 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include <../lib/pengine/unpack.h> /* GMainLoop *mainloop = NULL; */ void wait_for_refresh(int offset, const char *prefix, int msec); void clean_up(int rc); void crm_diff_update(const char *event, xmlNode *msg); gboolean mon_refresh_display(gpointer user_data); int cib_connect(gboolean full); char *xml_file = NULL; char *as_html_file = NULL; char *pid_file = NULL; char *snmp_target = NULL; char *snmp_community = NULL; gboolean as_console = TRUE;; gboolean simple_status = FALSE; gboolean group_by_node = FALSE; gboolean inactive_resources = FALSE; gboolean web_cgi = FALSE; int reconnect_msec = 5000; gboolean daemonize = FALSE; GMainLoop *mainloop = NULL; guint timer_id = 0; GList *attr_list = NULL; const char *crm_mail_host = NULL; const char *crm_mail_prefix = NULL; const char *crm_mail_from = NULL; const char *crm_mail_to = NULL; const char *external_agent = NULL; const char *external_recipient = NULL; cib_t *cib = NULL; xmlNode *current_cib = NULL; gboolean one_shot = FALSE; gboolean has_warnings = FALSE; gboolean print_failcount = FALSE; gboolean print_operations = FALSE; gboolean print_timing = FALSE; gboolean print_nodes_attr = FALSE; #define FILTER_STR {"shutdown", "terminate", "standby", "fail-count", \ "last-failure", "probe_complete", "#id", "#uname", \ "#is_dc", NULL} gboolean log_diffs = FALSE; gboolean log_updates = FALSE; long last_refresh = 0; crm_trigger_t *refresh_trigger = NULL; /* * 1.3.6.1.4.1.32723 has been assigned to the project by IANA * http://www.iana.org/assignments/enterprise-numbers */ #define PACEMAKER_PREFIX "1.3.6.1.4.1.32723" #define PACEMAKER_TRAP_PREFIX PACEMAKER_PREFIX ".1" #define snmp_crm_trap_oid PACEMAKER_TRAP_PREFIX #define snmp_crm_oid_node PACEMAKER_TRAP_PREFIX ".1" #define snmp_crm_oid_rsc PACEMAKER_TRAP_PREFIX ".2" #define snmp_crm_oid_task PACEMAKER_TRAP_PREFIX ".3" #define snmp_crm_oid_desc PACEMAKER_TRAP_PREFIX ".4" #define snmp_crm_oid_status PACEMAKER_TRAP_PREFIX ".5" #define snmp_crm_oid_rc PACEMAKER_TRAP_PREFIX ".6" #define snmp_crm_oid_trc PACEMAKER_TRAP_PREFIX ".7" #if CURSES_ENABLED # define print_dot() if(as_console) { \ printw("."); \ clrtoeol(); \ refresh(); \ } else { \ fprintf(stdout, "."); \ } #else # define print_dot() fprintf(stdout, "."); #endif #if CURSES_ENABLED # define print_as(fmt, args...) if(as_console) { \ printw(fmt, ##args); \ clrtoeol(); \ refresh(); \ } else { \ fprintf(stdout, fmt, ##args); \ } #else # define print_as(fmt, args...) fprintf(stdout, fmt, ##args); #endif static void blank_screen(void) { #if CURSES_ENABLED int lpc = 0; for(lpc = 0; lpc < LINES; lpc++) { move(lpc, 0); clrtoeol(); } move(0, 0); refresh(); #endif } static gboolean mon_timer_popped(gpointer data) { int rc = cib_ok; if(timer_id > 0) { g_source_remove(timer_id); } rc = cib_connect(TRUE); if(rc != cib_ok) { print_dot(); timer_id = g_timeout_add(reconnect_msec, mon_timer_popped, NULL); } return FALSE; } static void mon_cib_connection_destroy(gpointer user_data) { print_as("Connection to the CIB terminated\n"); if(cib) { print_as("Reconnecting..."); cib->cmds->signoff(cib); timer_id = g_timeout_add(reconnect_msec, mon_timer_popped, NULL); } return; } /* * Mainloop signal handler. */ static void mon_shutdown(int nsig) { clean_up(LSB_EXIT_OK); } #if ON_DARWIN # define sighandler_t sig_t #endif #if CURSES_ENABLED static sighandler_t ncurses_winch_handler; static void mon_winresize(int nsig) { static int not_done; int lines = 0, cols = 0; if(!not_done++) { if(ncurses_winch_handler) /* the original ncurses WINCH signal handler does the * magic of retrieving the new window size; * otherwise, we'd have to use ioctl or tgetent */ (*ncurses_winch_handler) (SIGWINCH); getmaxyx(stdscr, lines, cols); resizeterm(lines,cols); mainloop_set_trigger(refresh_trigger); } not_done--; } #endif int cib_connect(gboolean full) { int rc = cib_ok; static gboolean need_pass = TRUE; CRM_CHECK(cib != NULL, return cib_missing); if(getenv("CIB_passwd") != NULL) { need_pass = FALSE; } if(cib->state != cib_connected_query && cib->state != cib_connected_command) { crm_debug_4("Connecting to the CIB"); if(as_console && need_pass && cib->variant == cib_remote) { need_pass = FALSE; print_as("Password:"); } rc = cib->cmds->signon(cib, crm_system_name, cib_query); if(rc != cib_ok) { return rc; } current_cib = get_cib_copy(cib); mon_refresh_display(NULL); if(full) { if(rc == cib_ok) { rc = cib->cmds->set_connection_dnotify(cib, mon_cib_connection_destroy); if(rc == cib_NOTSUPPORTED) { print_as("Notification setup failed, won't be able to reconnect after failure"); if(as_console) { sleep(2); } rc = cib_ok; } } if(rc == cib_ok) { cib->cmds->del_notify_callback(cib, T_CIB_DIFF_NOTIFY, crm_diff_update); rc = cib->cmds->add_notify_callback(cib, T_CIB_DIFF_NOTIFY, crm_diff_update); } if(rc != cib_ok) { print_as("Notification setup failed, could not monitor CIB actions"); if(as_console) { sleep(2); } clean_up(-rc); } } } return rc; } static struct crm_option long_options[] = { /* Top-level Options */ {"help", 0, 0, '?', "\tThis text"}, {"version", 0, 0, '$', "\tVersion information" }, {"verbose", 0, 0, 'V', "\tIncrease debug output"}, {"-spacer-", 1, 0, '-', "\nModes:"}, {"as-html", 1, 0, 'h', "Write cluster status to the named file"}, {"web-cgi", 0, 0, 'w', "\tWeb mode with output suitable for cgi"}, {"simple-status", 0, 0, 's', "Display the cluster status once as a simple one line output (suitable for nagios)"}, {"snmp-traps", 1, 0, 'S', "Send SNMP traps to this station", !ENABLE_SNMP}, {"snmp-community", 1, 0, 'C', "Specify community for SNMP traps(default is NULL)", !ENABLE_SNMP}, {"mail-to", 1, 0, 'T', "Send Mail alerts to this user. See also --mail-from, --mail-host, --mail-prefix", !ENABLE_ESMTP}, {"-spacer-", 1, 0, '-', "\nDisplay Options:"}, {"group-by-node", 0, 0, 'n', "\tGroup resources by node" }, {"inactive", 0, 0, 'r', "\tDisplay inactive resources" }, {"failcounts", 0, 0, 'f', "\tDisplay resource fail counts"}, {"operations", 0, 0, 'o', "\tDisplay resource operation history" }, {"timing-details", 0, 0, 't', "\tDisplay resource operation history with timing details" }, {"show-node-attributes", 0, 0, 'A', "Display node attributes\n" }, {"-spacer-", 1, 0, '-', "\nAdditional Options:"}, {"interval", 1, 0, 'i', "\tUpdate frequency in seconds" }, {"one-shot", 0, 0, '1', "\tDisplay the cluster status once on the console and exit"}, {"disable-ncurses",0, 0, 'N', "\tDisable the use of ncurses", !CURSES_ENABLED}, {"daemonize", 0, 0, 'd', "\tRun in the background as a daemon"}, {"pid-file", 1, 0, 'p', "\t(Advanced) Daemon pid file location"}, {"mail-from", 1, 0, 'F', "\tMail alerts should come from the named user", !ENABLE_ESMTP}, {"mail-host", 1, 0, 'H', "\tMail alerts should be sent via the named host", !ENABLE_ESMTP}, {"mail-prefix", 1, 0, 'P', "Subjects for mail alerts should start with this string", !ENABLE_ESMTP}, {"external-agent", 1, 0, 'E', "A program to run when resource operations take place."}, {"external-recipient",1, 0, 'e', "A recipient for your program (assuming you want the program to send something to someone)."}, {"xml-file", 1, 0, 'x', NULL, 1}, {"-spacer-", 1, 0, '-', "\nExamples:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', "Display the cluster´s status on the console with updates as they occur:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_mon", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Display the cluster´s status on the console just once then exit:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_mon -1", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Display your cluster´s status, group resources by node, and include inactive resources in the list:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_mon --group-by-node --inactive", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Start crm_mon as a background daemon and have it write the cluster´s status to an HTML file:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_mon --daemonize --as-html /path/to/docroot/filename.html", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Start crm_mon as a background daemon and have it send email alerts:", pcmk_option_paragraph|!ENABLE_ESMTP}, {"-spacer-", 1, 0, '-', " crm_mon --daemonize --mail-to user@example.com --mail-host mail.example.com", pcmk_option_example|!ENABLE_ESMTP}, {"-spacer-", 1, 0, '-', "Start crm_mon as a background daemon and have it send SNMP alerts:", pcmk_option_paragraph|!ENABLE_SNMP}, {"-spacer-", 1, 0, '-', " crm_mon --daemonize --snmp-traps snmptrapd.example.com", pcmk_option_example|!ENABLE_SNMP}, {NULL, 0, 0, 0} }; int main(int argc, char **argv) { int flag; int argerr = 0; int exit_code = 0; int option_index = 0; pid_file = crm_strdup("/tmp/ClusterMon.pid"); crm_log_init_quiet(NULL, LOG_CRIT, FALSE, FALSE, argc, argv); crm_set_options("V?$i:nrh:dp:s1wx:oftANS:T:F:H:P:E:e:C:", "mode [options]", long_options, "Provides a summary of cluster's current state." "\n\nOutputs varying levels of detail in a number of different formats.\n"); #ifndef ON_DARWIN /* prevent zombies */ signal(SIGCLD, SIG_IGN); #endif if (strcmp(crm_system_name, "crm_mon.cgi")==0) { web_cgi = TRUE; one_shot = TRUE; } while (1) { flag = crm_get_option(argc, argv, &option_index); if (flag == -1) break; switch(flag) { case 'V': cl_log_enable_stderr(TRUE); alter_debug(DEBUG_INC); break; case 'i': reconnect_msec = crm_get_msec(optarg); break; case 'n': group_by_node = TRUE; break; case 'r': inactive_resources = TRUE; break; case 'd': daemonize = TRUE; break; case 't': print_timing = TRUE; print_operations = TRUE; break; case 'o': print_operations = TRUE; break; case 'f': print_failcount = TRUE; break; case 'A': print_nodes_attr = TRUE; break; case 'p': crm_free(pid_file); pid_file = crm_strdup(optarg); break; case 'x': xml_file = crm_strdup(optarg); one_shot = TRUE; break; case 'h': as_html_file = crm_strdup(optarg); break; case 'w': web_cgi = TRUE; one_shot = TRUE; break; case 's': simple_status = TRUE; one_shot = TRUE; break; case 'S': snmp_target = optarg; break; case 'T': crm_mail_to = optarg; break; case 'F': crm_mail_from = optarg; break; case 'H': crm_mail_host = optarg; break; case 'P': crm_mail_prefix = optarg; break; case 'E': external_agent = optarg; break; case 'e': external_recipient = optarg; break; case '1': one_shot = TRUE; break; case 'N': as_console = FALSE; break; case 'C': snmp_community = optarg; break; case '$': case '?': crm_help(flag, LSB_EXIT_OK); break; default: printf("Argument code 0%o (%c) is not (?yet?) supported\n", flag, flag); ++argerr; break; } } if (optind < argc) { printf("non-option ARGV-elements: "); while (optind < argc) printf("%s ", argv[optind++]); printf("\n"); } if (argerr) { crm_help('?', LSB_EXIT_GENERIC); } if(one_shot) { as_console = FALSE; } else if(daemonize) { as_console = FALSE; cl_log_enable_stderr(FALSE); if(!as_html_file && !snmp_target && !crm_mail_to && !external_agent) { printf("Looks like you forgot to specify one or more of: --as-html, --mail-to, --snmp-target, --external-agent\n"); crm_help('?', LSB_EXIT_GENERIC); } crm_make_daemon(crm_system_name, TRUE, pid_file); } else if(as_console) { #if CURSES_ENABLED initscr(); cbreak(); noecho(); cl_log_enable_stderr(FALSE); #else one_shot = TRUE; as_console = FALSE; printf("Defaulting to one-shot mode\n"); printf("You need to have curses available at compile time to enable console mode\n"); #endif } crm_info("Starting %s", crm_system_name); if(xml_file != NULL) { current_cib = filename2xml(xml_file); mon_refresh_display(NULL); return exit_code; } if(current_cib == NULL) { cib = cib_new(); if(!one_shot) { print_as("Attempting connection to the cluster..."); } do { exit_code = cib_connect(!one_shot); if(one_shot) { break; } else if(exit_code != cib_ok) { print_dot(); sleep(reconnect_msec/1000); } } while(exit_code == cib_connection); if(exit_code != cib_ok) { print_as("\nConnection to cluster failed: %s\n", cib_error2string(exit_code)); if(as_console) { sleep(2); } clean_up(-exit_code); } } if(one_shot) { return exit_code; } mainloop = g_main_new(FALSE); mainloop_add_signal(SIGTERM, mon_shutdown); mainloop_add_signal(SIGINT, mon_shutdown); #if CURSES_ENABLED if(as_console) { ncurses_winch_handler = signal(SIGWINCH, mon_winresize); if(ncurses_winch_handler == SIG_DFL || ncurses_winch_handler == SIG_IGN || ncurses_winch_handler == SIG_ERR) ncurses_winch_handler = NULL; } #endif refresh_trigger = mainloop_add_trigger(G_PRIORITY_LOW, mon_refresh_display, NULL); g_main_run(mainloop); g_main_destroy(mainloop); crm_info("Exiting %s", crm_system_name); clean_up(0); return 0; /* never reached */ } void wait_for_refresh(int offset, const char *prefix, int msec) { int lpc = msec / 1000; struct timespec sleept = {1 , 0}; if(as_console == FALSE) { timer_id = g_timeout_add(msec, mon_timer_popped, NULL); return; } crm_notice("%sRefresh in %ds...", prefix?prefix:"", lpc); while(lpc > 0) { #if CURSES_ENABLED move(offset, 0); /* printw("%sRefresh in \033[01;32m%ds\033[00m...", prefix?prefix:"", lpc); */ printw("%sRefresh in %ds...\n", prefix?prefix:"", lpc); clrtoeol(); refresh(); #endif lpc--; if(lpc == 0) { timer_id = g_timeout_add( 1000, mon_timer_popped, NULL); } else { if (nanosleep(&sleept, NULL) != 0) { return; } } } } #define mon_warn(fmt...) do { \ if (!has_warnings) { \ print_as("Warning:"); \ } else { \ print_as(","); \ } \ print_as(fmt); \ has_warnings = TRUE; \ } while(0) static int print_simple_status(pe_working_set_t *data_set) { node_t *dc = NULL; GListPtr gIter = NULL; int nodes_online = 0; int nodes_standby = 0; dc = data_set->dc_node; if(dc == NULL) { mon_warn("No DC "); } for(gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t*)gIter->data; if(node->details->standby && node->details->online) { nodes_standby++; } else if(node->details->online) { nodes_online++; } else { mon_warn("offline node: %s", node->details->uname); } } if (!has_warnings) { print_as("Ok: %d nodes online", nodes_online); if (nodes_standby > 0) { print_as(", %d standby nodes", nodes_standby); } print_as(", %d resources configured", g_list_length(data_set->resources)); } print_as("\n"); return 0; } extern int get_failcount(node_t *node, resource_t *rsc, int *last_failure, pe_working_set_t *data_set); static void print_date(time_t time) { int lpc = 0; char date_str[26]; asctime_r(localtime(&time), date_str); for(; lpc < 26; lpc++) { if(date_str[lpc] == '\n') { date_str[lpc] = 0; } } print_as("'%s'", date_str); } static void print_rsc_summary(pe_working_set_t *data_set, node_t *node, resource_t *rsc, gboolean all) { gboolean printed = FALSE; time_t last_failure = 0; char *fail_attr = crm_concat("fail-count", rsc->id, '-'); const char *value = g_hash_table_lookup(node->details->attrs, fail_attr); int failcount = char2score(value); /* Get the true value, not the effective one from get_failcount() */ get_failcount(node, rsc, (int*)&last_failure, data_set); crm_free(fail_attr); if(all || failcount || last_failure > 0) { printed = TRUE; print_as(" %s: migration-threshold=%d", rsc->id, rsc->migration_threshold); } if(failcount > 0) { printed = TRUE; print_as(" fail-count=%d", failcount); } if(last_failure > 0) { printed = TRUE; print_as(" last-failure="); print_date(last_failure); } if(printed) { print_as("\n"); } } static void print_rsc_history(pe_working_set_t *data_set, node_t *node, xmlNode *rsc_entry) { GListPtr gIter = NULL; GListPtr op_list = NULL; gboolean print_name = TRUE; GListPtr sorted_op_list = NULL; const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID); resource_t *rsc = pe_find_resource(data_set->resources, rsc_id); - xml_child_iter_filter( - rsc_entry, rsc_op, XML_LRM_TAG_RSC_OP, - op_list = g_list_append(op_list, rsc_op); - ); + xmlNode *rsc_op = NULL; + for(rsc_op = rsc_entry; rsc_op != NULL; rsc_op = rsc_op->next) { + if(crm_str_eq((const char *)rsc_op->name, XML_LRM_TAG_RSC_OP, TRUE)) { + op_list = g_list_append(op_list, rsc_op); + } + } sorted_op_list = g_list_sort(op_list, sort_op_by_callid); for(gIter = sorted_op_list; gIter != NULL; gIter = gIter->next) { xmlNode *xml_op = (xmlNode*)gIter->data; const char *value = NULL; const char *call = crm_element_value(xml_op, XML_LRM_ATTR_CALLID); const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK); const char *op_rc = crm_element_value(xml_op, XML_LRM_ATTR_RC); const char *interval = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL); int rc = crm_parse_int(op_rc, "0"); if(safe_str_eq(task, CRMD_ACTION_STATUS) && safe_str_eq(interval, "0")) { task = "probe"; } if(rc == 7 && safe_str_eq(task, "probe")) { continue; } else if(safe_str_eq(task, CRMD_ACTION_NOTIFY)) { continue; } if(print_name) { print_name = FALSE; if(rsc == NULL) { print_as("Orphan resource: %s", rsc_id); } else { print_rsc_summary(data_set, node, rsc, TRUE); } } print_as(" + (%s) %s:", call, task); if(safe_str_neq(interval, "0")) { print_as(" interval=%sms", interval); } if(print_timing) { int int_value; const char *attr = "last-rc-change"; value = crm_element_value(xml_op, attr); if(value) { int_value = crm_parse_int(value, NULL); print_as(" %s=", attr); print_date(int_value); } attr = "last-run"; value = crm_element_value(xml_op, attr); if(value) { int_value = crm_parse_int(value, NULL); print_as(" %s=", attr); print_date(int_value); } attr = "exec-time"; value = crm_element_value(xml_op, attr); if(value) { int_value = crm_parse_int(value, NULL); print_as(" %s=%dms", attr, int_value); } attr = "queue-time"; value = crm_element_value(xml_op, attr); if(value) { int_value = crm_parse_int(value, NULL); print_as(" %s=%dms", attr, int_value); } } print_as(" rc=%s (%s)\n", op_rc, execra_code2string(rc)); } /* no need to free the contents */ g_list_free(sorted_op_list); } static void print_attr_msg(node_t *node, GListPtr rsc_list, const char *attrname, const char *attrvalue) { GListPtr gIter = NULL; for(gIter = rsc_list; gIter != NULL; gIter = gIter->next) { resource_t *rsc = (resource_t*)gIter->data; const char *type = g_hash_table_lookup(rsc->meta, "type"); if(rsc->children != NULL) { print_attr_msg(node, rsc->children, attrname, attrvalue); } if(safe_str_eq(type, "ping") || safe_str_eq(type, "pingd")) { const char *name = "pingd"; const char *multiplier = NULL; char **host_list = NULL; int host_list_num = 0; int expected_score = 0; if(g_hash_table_lookup(rsc->meta, "name") != NULL) { name = g_hash_table_lookup(rsc->meta, "name"); } /* To identify the resource with the attribute name. */ if(safe_str_eq(name, attrname)) { int value = crm_parse_int(attrvalue, "0"); multiplier = g_hash_table_lookup(rsc->meta, "multiplier"); host_list = g_strsplit(g_hash_table_lookup(rsc->meta, "host_list"), " ", 0); host_list_num = g_strv_length(host_list); g_strfreev(host_list); /* pingd multiplier is the same as the default value. */ expected_score = host_list_num * crm_parse_int(multiplier, "1"); /* pingd is abnormal score. */ if(value <= 0) { print_as("\t: Connectivity is lost"); } else if(value < expected_score) { print_as("\t: Connectivity is degraded (Expected=%d)", expected_score); } } } } } static int compare_attribute(gconstpointer a, gconstpointer b) { int rc; rc = strcmp((const char*)a, (const char*)b); return rc; } static void create_attr_list(gpointer name, gpointer value, gpointer data) { int i; const char *filt_str[] = FILTER_STR; CRM_CHECK(name != NULL, return); /* filtering automatic attributes */ for(i = 0; filt_str[i] != NULL; i++) { if(g_str_has_prefix(name, filt_str[i])) { return; } } attr_list = g_list_insert_sorted(attr_list, name, compare_attribute); } static void print_node_attribute(gpointer name, gpointer node_data) { const char *value = NULL; node_t *node = (node_t *)node_data; value = g_hash_table_lookup(node->details->attrs, name); print_as(" + %-32s\t: %-10s", (char *)name, value); print_attr_msg(node, node->details->running_rsc, name, value); print_as("\n"); } static void print_node_summary(pe_working_set_t *data_set, gboolean operations) { xmlNode *lrm_rsc = NULL; + xmlNode *rsc_entry = NULL; + xmlNode *node_state = NULL; xmlNode *cib_status = get_object_root(XML_CIB_TAG_STATUS, data_set->input); if(operations) { print_as("\nOperations:\n"); } else { print_as("\nMigration summary:\n"); } - xml_child_iter_filter( - cib_status, node_state, XML_CIB_TAG_STATE, - node_t *node = pe_find_node_id(data_set->nodes, ID(node_state)); - if(node == NULL || node->details->online == FALSE){ - continue; - } + for(node_state = cib_status; node_state != NULL; node_state = node_state->next) { + if(crm_str_eq((const char *)node_state->name, XML_CIB_TAG_STATE, TRUE)) { + node_t *node = pe_find_node_id(data_set->nodes, ID(node_state)); + if(node == NULL || node->details->online == FALSE){ + continue; + } - print_as("* Node %s: ", crm_element_value(node_state, XML_ATTR_UNAME)); - print_as("\n"); + print_as("* Node %s: ", crm_element_value(node_state, XML_ATTR_UNAME)); + print_as("\n"); - lrm_rsc = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE); - lrm_rsc = find_xml_node(lrm_rsc, XML_LRM_TAG_RESOURCES, FALSE); + lrm_rsc = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE); + lrm_rsc = find_xml_node(lrm_rsc, XML_LRM_TAG_RESOURCES, FALSE); - xml_child_iter_filter( - lrm_rsc, rsc_entry, XML_LRM_TAG_RESOURCE, - - if(operations) { - print_rsc_history(data_set, node, rsc_entry); + for(rsc_entry = lrm_rsc; rsc_entry != NULL; rsc_entry = rsc_entry->next) { + if(crm_str_eq((const char *)rsc_entry->name, XML_LRM_TAG_RESOURCE, TRUE)) { + - } else { - const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID); - resource_t *rsc = pe_find_resource(data_set->resources, rsc_id); - if(rsc) { - print_rsc_summary(data_set, node, rsc, FALSE); - } else { - print_as(" %s: orphan\n", rsc_id); + if(operations) { + print_rsc_history(data_set, node, rsc_entry); + + } else { + const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID); + resource_t *rsc = pe_find_resource(data_set->resources, rsc_id); + if(rsc) { + print_rsc_summary(data_set, node, rsc, FALSE); + } else { + print_as(" %s: orphan\n", rsc_id); + } + } } } - ); - ); + } + } } static char * add_list_element(char *list, const char *value) { int len = 0; int last = 0; if(value == NULL) { return list; } if(list) { last = strlen(list); } len = last + 2; /* +1 space, +1 EOS */ len += strlen(value); crm_realloc(list, len); sprintf(list + last, " %s", value); return list; } static int print_status(pe_working_set_t *data_set) { static int updates = 0; GListPtr gIter = NULL; node_t *dc = NULL; char *since_epoch = NULL; char *online_nodes = NULL; char *offline_nodes = NULL; xmlNode *dc_version = NULL; xmlNode *quorum_node = NULL; xmlNode *stack = NULL; time_t a_time = time(NULL); int configured_resources = 0; int print_opts = pe_print_ncurses; const char *quorum_votes = "unknown"; if(as_console) { blank_screen(); } else { print_opts = pe_print_printf; } updates++; dc = data_set->dc_node; print_as("============\n"); if(a_time == (time_t)-1) { crm_perror(LOG_ERR,"set_node_tstamp(): Invalid time returned"); return 1; } since_epoch = ctime(&a_time); if(since_epoch != NULL) { print_as("Last updated: %s", since_epoch); } stack = get_xpath_object("//nvpair[@name='cluster-infrastructure']", data_set->input, LOG_DEBUG); if(stack) { print_as("Stack: %s\n", crm_element_value(stack, XML_NVPAIR_ATTR_VALUE)); } dc_version = get_xpath_object("//nvpair[@name='dc-version']", data_set->input, LOG_DEBUG); if(dc == NULL) { print_as("Current DC: NONE\n"); } else { const char *quorum = crm_element_value(data_set->input, XML_ATTR_HAVE_QUORUM); if(safe_str_neq(dc->details->uname, dc->details->id)) { print_as("Current DC: %s (%s)", dc->details->uname, dc->details->id); } else { print_as("Current DC: %s", dc->details->uname); } print_as(" - partition %s quorum\n", crm_is_true(quorum)?"with":"WITHOUT"); if(dc_version) { print_as("Version: %s\n", crm_element_value(dc_version, XML_NVPAIR_ATTR_VALUE)); } } quorum_node = get_xpath_object("//nvpair[@name='"XML_ATTR_EXPECTED_VOTES"']", data_set->input, LOG_DEBUG); if(quorum_node) { quorum_votes = crm_element_value(quorum_node, XML_NVPAIR_ATTR_VALUE); } for(gIter = data_set->resources; gIter != NULL; gIter = gIter->next) { resource_t *rsc = (resource_t*)gIter->data; if(is_not_set(rsc->flags, pe_rsc_orphan)) { configured_resources++; } } print_as("%d Nodes configured, %s expected votes\n", g_list_length(data_set->nodes), quorum_votes); print_as("%d Resources configured.\n", configured_resources); print_as("============\n\n"); for(gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t*)gIter->data; const char *node_mode = NULL; if(node->details->unclean) { if(node->details->online && node->details->unclean) { node_mode = "UNCLEAN (online)"; } else if(node->details->pending) { node_mode = "UNCLEAN (pending)"; } else { node_mode = "UNCLEAN (offline)"; } } else if(node->details->pending) { node_mode = "pending"; } else if(node->details->standby_onfail && node->details->online) { node_mode = "standby (on-fail)"; } else if(node->details->standby) { if(node->details->online) { node_mode = "standby"; } else { node_mode = "OFFLINE (standby)"; } } else if(node->details->online) { node_mode = "online"; if(group_by_node == FALSE) { online_nodes = add_list_element(online_nodes, node->details->uname); continue; } } else { node_mode = "OFFLINE"; if(group_by_node == FALSE) { offline_nodes = add_list_element(offline_nodes, node->details->uname); continue; } } if(safe_str_eq(node->details->uname, node->details->id)) { print_as("Node %s: %s\n", node->details->uname, node_mode); } else { print_as("Node %s (%s): %s\n", node->details->uname, node->details->id, node_mode); } if(group_by_node) { GListPtr gIter2 = NULL; for(gIter2 = node->details->running_rsc; gIter2 != NULL; gIter2 = gIter2->next) { resource_t *rsc = (resource_t*)gIter2->data; rsc->fns->print(rsc, "\t", print_opts|pe_print_rsconly, stdout); } } } if(online_nodes) { print_as("Online: [%s ]\n", online_nodes); crm_free(online_nodes); } if(offline_nodes) { print_as("OFFLINE: [%s ]\n", offline_nodes); crm_free(offline_nodes); } if(group_by_node == FALSE && inactive_resources) { print_as("\nFull list of resources:\n"); } else if(inactive_resources) { print_as("\nInactive resources:\n"); } if(group_by_node == FALSE || inactive_resources) { print_as("\n"); for(gIter = data_set->resources; gIter != NULL; gIter = gIter->next) { resource_t *rsc = (resource_t*)gIter->data; gboolean is_active = rsc->fns->active(rsc, TRUE); gboolean partially_active = rsc->fns->active(rsc, FALSE); if(is_set(rsc->flags, pe_rsc_orphan) && is_active == FALSE) { continue; } else if(group_by_node == FALSE) { if(partially_active || inactive_resources) { rsc->fns->print(rsc, NULL, print_opts, stdout); } } else if(is_active == FALSE && inactive_resources) { rsc->fns->print(rsc, NULL, print_opts, stdout); } } } if(print_nodes_attr) { print_as("\nNode Attributes:\n"); for(gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t*)gIter->data; if(node == NULL || node->details->online == FALSE){ continue; } attr_list = NULL; print_as("* Node %s:\n", node->details->uname); g_hash_table_foreach(node->details->attrs, create_attr_list, NULL); g_list_foreach(attr_list, print_node_attribute, node); } } if(print_operations || print_failcount) { print_node_summary(data_set, print_operations); } if(xml_has_children(data_set->failed)) { + xmlNode *xml_op = NULL; print_as("\nFailed actions:\n"); - xml_child_iter(data_set->failed, xml_op, - int val = 0; - const char *id = ID(xml_op); - const char *last = crm_element_value(xml_op, "last_run"); - const char *node = crm_element_value(xml_op, XML_ATTR_UNAME); - const char *call = crm_element_value(xml_op, XML_LRM_ATTR_CALLID); - const char *rc = crm_element_value(xml_op, XML_LRM_ATTR_RC); - const char *status = crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS); + for(xml_op = data_set->failed; xml_op != NULL; xml_op = xml_op->next) { + int val = 0; + const char *id = ID(xml_op); + const char *last = crm_element_value(xml_op, "last_run"); + const char *node = crm_element_value(xml_op, XML_ATTR_UNAME); + const char *call = crm_element_value(xml_op, XML_LRM_ATTR_CALLID); + const char *rc = crm_element_value(xml_op, XML_LRM_ATTR_RC); + const char *status = crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS); - val = crm_parse_int(status, "0"); - print_as(" %s (node=%s, call=%s, rc=%s, status=%s", - id, node, call, rc, op_status2text(val)); - - if(last) { - time_t run_at = crm_parse_int(last, "0"); - print_as(", last-run=%s, queued=%sms, exec=%sms\n", - ctime(&run_at), - crm_element_value(xml_op, "exec_time"), - crm_element_value(xml_op, "queue_time")); - } - - val = crm_parse_int(rc, "0"); - print_as("): %s\n", execra_code2string(val)); - ); + val = crm_parse_int(status, "0"); + print_as(" %s (node=%s, call=%s, rc=%s, status=%s", + id, node, call, rc, op_status2text(val)); + + if(last) { + time_t run_at = crm_parse_int(last, "0"); + print_as(", last-run=%s, queued=%sms, exec=%sms\n", + ctime(&run_at), + crm_element_value(xml_op, "exec_time"), + crm_element_value(xml_op, "queue_time")); + } + + val = crm_parse_int(rc, "0"); + print_as("): %s\n", execra_code2string(val)); + } } #if CURSES_ENABLED if(as_console) { refresh(); } #endif return 0; } static int print_html_status(pe_working_set_t *data_set, const char *filename, gboolean web_cgi) { FILE *stream; GListPtr gIter = NULL; node_t *dc = NULL; static int updates = 0; char *filename_tmp = NULL; if (web_cgi) { stream=stdout; fprintf(stream, "Content-type: text/html\n\n"); } else { filename_tmp = crm_concat(filename, "tmp", '.'); stream = fopen(filename_tmp, "w"); if(stream == NULL) { crm_perror(LOG_ERR,"Cannot open %s for writing", filename_tmp); crm_free(filename_tmp); return -1; } } updates++; dc = data_set->dc_node; fprintf(stream, ""); fprintf(stream, ""); fprintf(stream, "Cluster status"); /* content="%d;url=http://webdesign.about.com" */ fprintf(stream, "", reconnect_msec/1000); fprintf(stream, ""); /*** SUMMARY ***/ fprintf(stream, "

    Cluster summary

    "); { char *now_str = NULL; time_t now = time(NULL); now_str = ctime(&now); now_str[24] = EOS; /* replace the newline */ fprintf(stream, "Last updated: %s
    \n", now_str); } if(dc == NULL) { fprintf(stream, "Current DC: NONE
    "); } else { fprintf(stream, "Current DC: %s (%s)
    ", dc->details->uname, dc->details->id); } fprintf(stream, "%d Nodes configured.
    ", g_list_length(data_set->nodes)); fprintf(stream, "%d Resources configured.
    ", g_list_length(data_set->resources)); /*** CONFIG ***/ fprintf(stream, "

    Config Options

    \n"); fprintf(stream, "\n"); fprintf(stream, "\n", is_set(data_set->flags, pe_flag_stonith_enabled)?"enabled":"disabled"); fprintf(stream, "\n", is_set(data_set->flags, pe_flag_symmetric_cluster)?"":"a-"); fprintf(stream, "\n
    STONITH of failed nodes:%s
    Cluster is:%ssymmetric
    No Quorum Policy:"); switch (data_set->no_quorum_policy) { case no_quorum_freeze: fprintf(stream, "Freeze resources"); break; case no_quorum_stop: fprintf(stream, "Stop ALL resources"); break; case no_quorum_ignore: fprintf(stream, "Ignore"); break; case no_quorum_suicide: fprintf(stream, "Suicide"); break; } fprintf(stream, "\n
    \n"); /*** NODE LIST ***/ fprintf(stream, "

    Node List

    \n"); fprintf(stream, "
      \n"); for(gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t*)gIter->data; fprintf(stream, "
    • "); if(node->details->standby_onfail && node->details->online) { fprintf(stream, "Node: %s (%s): %s",node->details->uname, node->details->id,"standby (on-fail)\n"); } else if(node->details->standby && node->details->online) { fprintf(stream, "Node: %s (%s): %s",node->details->uname, node->details->id,"standby\n"); } else if(node->details->standby) { fprintf(stream, "Node: %s (%s): %s",node->details->uname, node->details->id,"OFFLINE (standby)\n"); } else if(node->details->online) { fprintf(stream, "Node: %s (%s): %s",node->details->uname, node->details->id,"online\n"); } else { fprintf(stream, "Node: %s (%s): %s",node->details->uname, node->details->id,"OFFLINE\n"); } if(group_by_node) { GListPtr lpc2 = NULL; fprintf(stream, "
        \n"); for(lpc2 = node->details->running_rsc; lpc2 != NULL; lpc2 = lpc2->next) { resource_t *rsc = (resource_t*)lpc2->data; fprintf(stream, "
      • "); rsc->fns->print(rsc, NULL, pe_print_html|pe_print_rsconly, stream); fprintf(stream, "
      • \n"); } fprintf(stream, "
      \n"); } fprintf(stream, "
    • \n"); } fprintf(stream, "
    \n"); if(group_by_node && inactive_resources) { fprintf(stream, "

    Inactive Resources

    \n"); } else if(group_by_node == FALSE) { fprintf(stream, "

    Resource List

    \n"); } if(group_by_node == FALSE || inactive_resources) { for(gIter = data_set->resources; gIter != NULL; gIter = gIter->next) { resource_t *rsc = (resource_t*)gIter->data; gboolean is_active = rsc->fns->active(rsc, TRUE); gboolean partially_active = rsc->fns->active(rsc, FALSE); if(is_set(rsc->flags, pe_rsc_orphan) && is_active == FALSE) { continue; } else if(group_by_node == FALSE) { if(partially_active || inactive_resources) { rsc->fns->print(rsc, NULL, pe_print_html, stream); } } else if(is_active == FALSE && inactive_resources) { rsc->fns->print(rsc, NULL, pe_print_html, stream); } } } fprintf(stream, ""); fflush(stream); fclose(stream); if (!web_cgi) { if(rename(filename_tmp, filename) != 0) { crm_perror(LOG_ERR,"Unable to rename %s->%s", filename_tmp, filename); } crm_free(filename_tmp); } return 0; } #if ENABLE_SNMP #include #include #include #include #include #include #define add_snmp_field(list, oid_string, value) do { \ oid name[MAX_OID_LEN]; \ size_t name_length = MAX_OID_LEN; \ if (snmp_parse_oid(oid_string, name, &name_length)) { \ int s_rc = snmp_add_var(list, name, name_length, 's', (value)); \ if(s_rc != 0) { \ crm_err("Could not add %s=%s rc=%d", oid_string, value, s_rc); \ } else { \ crm_debug_2("Added %s=%s", oid_string, value); \ } \ } else { \ crm_err("Could not parse OID: %s", oid_string); \ } \ } while(0) \ #define add_snmp_field_int(list, oid_string, value) do { \ oid name[MAX_OID_LEN]; \ size_t name_length = MAX_OID_LEN; \ if (snmp_parse_oid(oid_string, name, &name_length)) { \ if(NULL == snmp_pdu_add_variable( \ list, name, name_length, ASN_INTEGER, \ (u_char *) & value, sizeof(value))) { \ crm_err("Could not add %s=%d", oid_string, value); \ } else { \ crm_debug_2("Added %s=%d", oid_string, value); \ } \ } else { \ crm_err("Could not parse OID: %s", oid_string); \ } \ } while(0) \ static int snmp_input(int operation, netsnmp_session *session, int reqid, netsnmp_pdu *pdu, void *magic) { return 1; } static netsnmp_session *crm_snmp_init(const char *target, char *community) { static netsnmp_session *session = NULL; #ifdef NETSNMPV53 char target53[128]; snprintf(target53, sizeof(target53), "%s:162", target); #endif if(session) { return session; } if(target == NULL) { return NULL; } if(crm_log_level > LOG_INFO) { char *debug_tokens = crm_strdup("run:shell,snmptrap,tdomain"); debug_register_tokens(debug_tokens); snmp_set_do_debugging(1); } crm_malloc0(session, sizeof(netsnmp_session)); snmp_sess_init(session); session->version = SNMP_VERSION_2c; session->callback = snmp_input; session->callback_magic = NULL; if(community) { session->community_len = strlen(community); session->community = (unsigned char*)community; } session = snmp_add(session, #ifdef NETSNMPV53 netsnmp_tdomain_transport(target53, 0, "udp"), #else netsnmp_transport_open_client("snmptrap", target), #endif NULL, NULL); if (session == NULL) { snmp_sess_perror("Could not create snmp transport", session); } return session; } #endif static int send_snmp_trap(const char *node, const char *rsc, const char *task, int target_rc, int rc, int status, const char *desc) { int ret = 1; #if ENABLE_SNMP static oid snmptrap_oid[] = { 1,3,6,1,6,3,1,1,4,1,0 }; static oid sysuptime_oid[] = { 1,3,6,1,2,1,1,3,0 }; netsnmp_pdu *trap_pdu; netsnmp_session *session = crm_snmp_init(snmp_target, snmp_community); trap_pdu = snmp_pdu_create(SNMP_MSG_TRAP2); if ( !trap_pdu ) { crm_err("Failed to create SNMP notification"); return SNMPERR_GENERR; } if(1) { /* send uptime */ char csysuptime[20]; time_t now = time(NULL); sprintf(csysuptime, "%ld", now); snmp_add_var(trap_pdu, sysuptime_oid, sizeof(sysuptime_oid) / sizeof(oid), 't', csysuptime); } /* Indicate what the trap is by setting snmpTrapOid.0 */ ret = snmp_add_var(trap_pdu, snmptrap_oid, sizeof(snmptrap_oid) / sizeof(oid), 'o', snmp_crm_trap_oid); if (ret != 0) { crm_err("Failed set snmpTrapOid.0=%s", snmp_crm_trap_oid); return ret; } /* Add extries to the trap */ add_snmp_field(trap_pdu, snmp_crm_oid_rsc, rsc); add_snmp_field(trap_pdu, snmp_crm_oid_node, node); add_snmp_field(trap_pdu, snmp_crm_oid_task, task); add_snmp_field(trap_pdu, snmp_crm_oid_desc, desc); add_snmp_field_int(trap_pdu, snmp_crm_oid_rc, rc); add_snmp_field_int(trap_pdu, snmp_crm_oid_trc, target_rc); add_snmp_field_int(trap_pdu, snmp_crm_oid_status, status); /* Send and cleanup */ ret = snmp_send(session, trap_pdu); if(ret == 0) { /* error */ snmp_sess_perror("Could not send SNMP trap", session); snmp_free_pdu(trap_pdu); ret = SNMPERR_GENERR; } else { ret = SNMPERR_SUCCESS; } #else crm_err("Sending SNMP traps is not supported by this installation"); #endif return ret; } #if ENABLE_ESMTP #include #include static void print_recipient_status( smtp_recipient_t recipient, const char *mailbox, void *arg) { const smtp_status_t *status; status = smtp_recipient_status (recipient); printf ("%s: %d %s", mailbox, status->code, status->text); } static void event_cb (smtp_session_t session, int event_no, void *arg, ...) { int *ok; va_list alist; va_start(alist, arg); switch(event_no) { case SMTP_EV_CONNECT: case SMTP_EV_MAILSTATUS: case SMTP_EV_RCPTSTATUS: case SMTP_EV_MESSAGEDATA: case SMTP_EV_MESSAGESENT: case SMTP_EV_DISCONNECT: break; case SMTP_EV_WEAK_CIPHER: { int bits = va_arg(alist, long); ok = va_arg(alist, int*); crm_debug("SMTP_EV_WEAK_CIPHER, bits=%d - accepted.", bits); *ok = 1; break; } case SMTP_EV_STARTTLS_OK: crm_debug("SMTP_EV_STARTTLS_OK - TLS started here."); break; case SMTP_EV_INVALID_PEER_CERTIFICATE: { long vfy_result = va_arg(alist, long); ok = va_arg(alist, int*); /* There is a table in handle_invalid_peer_certificate() of mail-file.c */ crm_err("SMTP_EV_INVALID_PEER_CERTIFICATE: %ld", vfy_result); *ok = 1; break; } case SMTP_EV_NO_PEER_CERTIFICATE: ok = va_arg(alist, int*); crm_debug("SMTP_EV_NO_PEER_CERTIFICATE - accepted."); *ok = 1; break; case SMTP_EV_WRONG_PEER_CERTIFICATE: ok = va_arg(alist, int*); crm_debug("SMTP_EV_WRONG_PEER_CERTIFICATE - accepted."); *ok = 1; break; case SMTP_EV_NO_CLIENT_CERTIFICATE: ok = va_arg(alist, int*); crm_debug("SMTP_EV_NO_CLIENT_CERTIFICATE - accepted."); *ok = 1; break; default: crm_debug("Got event: %d - ignored.\n", event_no); } va_end(alist); } #endif #define BODY_MAX 2048 #if ENABLE_ESMTP static void crm_smtp_debug (const char *buf, int buflen, int writing, void *arg) { char type = 0; int lpc = 0, last = 0, level = *(int*)arg; if (writing == SMTP_CB_HEADERS) { type = 'H'; } else if(writing) { type = 'C'; } else { type = 'S'; } for(; lpc < buflen; lpc++) { switch(buf[lpc]) { case 0: case '\n': if(last > 0) { do_crm_log(level, " %.*s", lpc-last, buf+last); } else { do_crm_log(level, "%c: %.*s", type, lpc-last, buf+last); } last = lpc + 1; break; } } } #endif static int send_custom_trap(const char *node, const char *rsc, const char *task, int target_rc, int rc, int status, const char *desc) { pid_t pid; /*setenv needs chars, these are ints*/ char *rc_s = crm_itoa(rc); char *status_s = crm_itoa(status); char *target_rc_s = crm_itoa(target_rc); crm_debug("Sending external notification to '%s' via '%s'", external_recipient, external_agent); setenv("CRM_notify_recipient",external_recipient,1); setenv("CRM_notify_node",node,1); setenv("CRM_notify_rsc",rsc,1); setenv("CRM_notify_task",task,1); setenv("CRM_notify_desc",desc,1); setenv("CRM_notify_rc",rc_s,1); setenv("CRM_notify_target_rc",target_rc_s,1); setenv("CRM_notify_status",status_s,1); pid=fork(); if(pid == -1) { cl_perror("notification fork() failed."); } if(pid == 0) { /* crm_debug("notification: I am the child. Executing the nofitication program."); */ execl(external_agent,external_agent,NULL); } crm_debug_2("Finished running custom notification program '%s'.",external_agent); crm_free(target_rc_s); crm_free(status_s); crm_free(rc_s); return 0; } static int send_smtp_trap(const char *node, const char *rsc, const char *task, int target_rc, int rc, int status, const char *desc) { #if ENABLE_ESMTP smtp_session_t session; smtp_message_t message; auth_context_t authctx; struct sigaction sa; int len = 20; int noauth = 1; int smtp_debug = LOG_DEBUG; char crm_mail_body[BODY_MAX]; char *crm_mail_subject = NULL; if(node == NULL) { node = "-"; } if(rsc == NULL) { rsc = "-"; } if(desc == NULL) { desc = "-"; } if(crm_mail_to == NULL) { return 1; } if(crm_mail_host == NULL) { crm_mail_host = "localhost:25"; } if(crm_mail_prefix == NULL) { crm_mail_prefix = "Cluster notification"; } crm_debug("Sending '%s' mail to %s via %s", crm_mail_prefix, crm_mail_to, crm_mail_host); len += strlen(crm_mail_prefix); len += strlen(task); len += strlen(rsc); len += strlen(node); len += strlen(desc); len++; crm_malloc0(crm_mail_subject, len); snprintf(crm_mail_subject, len, "%s - %s event for %s on %s: %s\r\n", crm_mail_prefix, task, rsc, node, desc); len = 0; len += snprintf(crm_mail_body+len, BODY_MAX-len, "\r\n%s\r\n", crm_mail_prefix); len += snprintf(crm_mail_body+len, BODY_MAX-len, "====\r\n\r\n"); if(rc==target_rc) { len += snprintf(crm_mail_body+len, BODY_MAX-len, "Completed operation %s for resource %s on %s\r\n", task, rsc, node); } else { len += snprintf(crm_mail_body+len, BODY_MAX-len, "Operation %s for resource %s on %s failed: %s\r\n", task, rsc, node, desc); } len += snprintf(crm_mail_body+len, BODY_MAX-len, "\r\nDetails:\r\n"); len += snprintf(crm_mail_body+len, BODY_MAX-len, "\toperation status: (%d) %s\r\n", status, op_status2text(status)); if(status == LRM_OP_DONE) { len += snprintf(crm_mail_body+len, BODY_MAX-len, "\tscript returned: (%d) %s\r\n", rc, execra_code2string(rc)); len += snprintf(crm_mail_body+len, BODY_MAX-len, "\texpected return value: (%d) %s\r\n", target_rc, execra_code2string(target_rc)); } auth_client_init(); session = smtp_create_session(); message = smtp_add_message(session); smtp_starttls_enable (session, Starttls_ENABLED); sa.sa_handler = SIG_IGN; sigemptyset (&sa.sa_mask); sa.sa_flags = 0; sigaction (SIGPIPE, &sa, NULL); smtp_set_server (session, crm_mail_host); authctx = auth_create_context (); auth_set_mechanism_flags (authctx, AUTH_PLUGIN_PLAIN, 0); smtp_set_eventcb(session, event_cb, NULL); /* Now tell libESMTP it can use the SMTP AUTH extension. */ if (!noauth) { crm_debug("Adding authentication context"); smtp_auth_set_context (session, authctx); } if(crm_mail_from == NULL) { struct utsname us; char auto_from[BODY_MAX]; CRM_ASSERT(uname(&us) == 0); snprintf(auto_from, BODY_MAX, "crm_mon@%s", us.nodename); smtp_set_reverse_path (message, auto_from); } else { /* NULL is ok */ smtp_set_reverse_path (message, crm_mail_from); } smtp_set_header (message, "To", NULL/*phrase*/, NULL/*addr*/); /* "Phrase" */ smtp_add_recipient (message, crm_mail_to); /* Set the Subject: header and override any subject line in the message headers. */ smtp_set_header (message, "Subject", crm_mail_subject); smtp_set_header_option (message, "Subject", Hdr_OVERRIDE, 1); smtp_set_message_str(message, crm_mail_body); smtp_set_monitorcb (session, crm_smtp_debug, &smtp_debug, 1); if (smtp_start_session (session)) { char buf[128]; int rc = smtp_errno(); crm_err("SMTP server problem: %s (%d)", smtp_strerror (rc, buf, sizeof buf), rc); } else { char buf[128]; int rc = smtp_errno(); const smtp_status_t *smtp_status = smtp_message_transfer_status(message); if(rc != 0) { crm_err("SMTP server problem: %s (%d)", smtp_strerror (rc, buf, sizeof buf), rc); } crm_info("Send status: %d %s", smtp_status->code, crm_str(smtp_status->text)); smtp_enumerate_recipients (message, print_recipient_status, NULL); } smtp_destroy_session(session); auth_destroy_context(authctx); auth_client_exit(); #endif return 0; } static void handle_rsc_op(xmlNode *rsc_op) { int rc = -1; int status = -1; int action = -1; int interval = 0; int target_rc = -1; int transition_num = -1; gboolean notify = TRUE; char *rsc = NULL; char *task = NULL; const char *desc = NULL; const char *node = NULL; const char *magic = NULL; const char *id = ID(rsc_op); char *update_te_uuid = NULL; xmlNode *n = rsc_op; magic = crm_element_value(rsc_op, XML_ATTR_TRANSITION_MAGIC); if(magic == NULL) { /* non-change */ return; } if(FALSE == decode_transition_magic( magic, &update_te_uuid, &transition_num, &action, &status, &rc, &target_rc)) { crm_err("Invalid event %s detected for %s", magic, id); return; } if(parse_op_key(id, &rsc, &task, &interval) == FALSE) { crm_err("Invalid event detected for %s", id); goto bail; } while(n != NULL && safe_str_neq(XML_CIB_TAG_STATE, TYPE(n))) { n = n->parent; } node = crm_element_value(n, XML_ATTR_UNAME); if(node == NULL) { node = ID(n); } if(node == NULL) { crm_err("No node detected for event %s (%s)", magic, id); goto bail; } /* look up where we expected it to be? */ desc = cib_error2string(cib_ok); if(status == LRM_OP_DONE && target_rc == rc) { crm_notice("%s of %s on %s completed: %s", task, rsc, node, desc); if(rc == EXECRA_NOT_RUNNING) { notify = FALSE; } } else if(status == LRM_OP_DONE) { desc = execra_code2string(rc); crm_warn("%s of %s on %s failed: %s", task, rsc, node, desc); } else { desc = op_status2text(status); crm_warn("%s of %s on %s failed: %s", task, rsc, node, desc); } if(notify && snmp_target) { send_snmp_trap(node, rsc, task, target_rc, rc, status, desc); } if(notify && crm_mail_to) { send_smtp_trap(node, rsc, task, target_rc, rc, status, desc); } if(notify && external_agent) { send_custom_trap(node, rsc, task, target_rc, rc, status, desc); } bail: crm_free(update_te_uuid); crm_free(rsc); crm_free(task); } void crm_diff_update(const char *event, xmlNode *msg) { int rc = -1; long now = time(NULL); const char *op = NULL; unsigned int log_level = LOG_INFO; xmlNode *diff = NULL; xmlNode *cib_last = NULL; xmlNode *update = get_message_xml(msg, F_CIB_UPDATE); print_dot(); if(msg == NULL) { crm_err("NULL update"); return; } crm_element_value_int(msg, F_CIB_RC, &rc); op = crm_element_value(msg, F_CIB_OPERATION); diff = get_message_xml(msg, F_CIB_UPDATE_RESULT); if(rc < cib_ok) { log_level = LOG_WARNING; do_crm_log(log_level, "[%s] %s ABORTED: %s", event, op, cib_error2string(rc)); return; } if(current_cib != NULL) { cib_last = current_cib; current_cib = NULL; rc = cib_process_diff(op, cib_force_diff, NULL, NULL, diff, cib_last, ¤t_cib, NULL); if(rc != cib_ok) { crm_debug("Update didn't apply, requesting full copy: %s", cib_error2string(rc)); free_xml(current_cib); current_cib = NULL; } } if(current_cib == NULL) { current_cib = get_cib_copy(cib); } if(log_diffs && diff) { log_cib_diff(LOG_DEBUG, diff, op); } if(log_updates && update != NULL) { do_crm_log_xml(LOG_DEBUG, "raw_update", update); } if(diff && (crm_mail_to || snmp_target || external_agent)) { /* Process operation updates */ xmlXPathObject *xpathObj = xpath_search( diff, "//"F_CIB_UPDATE_RESULT"//"XML_TAG_DIFF_ADDED"//"XML_LRM_TAG_RSC_OP); if(xpathObj && xpathObj->nodesetval->nodeNr > 0) { int lpc = 0, max = xpathObj->nodesetval->nodeNr; for(lpc = 0; lpc < max; lpc++) { xmlNode *rsc_op = getXpathResult(xpathObj, lpc); handle_rsc_op(rsc_op); } } if (xpathObj) { xmlXPathFreeObject(xpathObj); } } if((now - last_refresh) > (reconnect_msec/1000)) { /* Force a refresh */ mon_refresh_display(NULL); } else { mainloop_set_trigger(refresh_trigger); } free_xml(cib_last); } gboolean mon_refresh_display(gpointer user_data) { xmlNode *cib_copy = copy_xml(current_cib); pe_working_set_t data_set; last_refresh = time(NULL); if(cli_config_update(&cib_copy, NULL, FALSE) == FALSE) { if(cib) { cib->cmds->signoff(cib); } print_as("Upgrade failed: %s", cib_error2string(cib_dtd_validation)); if(as_console) { sleep(2); } clean_up(LSB_EXIT_GENERIC); return FALSE; } set_working_set_defaults(&data_set); data_set.input = cib_copy; cluster_status(&data_set); if(as_html_file || web_cgi) { if (print_html_status(&data_set, as_html_file, web_cgi) != 0) { fprintf(stderr, "Critical: Unable to output html file\n"); clean_up(LSB_EXIT_GENERIC); } } else if(daemonize) { /* do nothing */ } else if (simple_status) { print_simple_status(&data_set); if (has_warnings) { clean_up(LSB_EXIT_GENERIC); } } else { print_status(&data_set); } cleanup_calculations(&data_set); return TRUE; } /* * De-init ncurses, signoff from the CIB and deallocate memory. */ void clean_up(int rc) { #if ENABLE_SNMP netsnmp_session *session = crm_snmp_init(NULL, NULL); if(session) { snmp_close(session); snmp_shutdown("snmpapp"); } #endif #if CURSES_ENABLED if(as_console) { as_console = FALSE; echo(); nocbreak(); endwin(); } #endif if (cib != NULL) { cib->cmds->signoff(cib); cib_delete(cib); cib = NULL; } crm_free(as_html_file); crm_free(xml_file); crm_free(pid_file); if(rc >= 0) { exit(rc); } return; } diff --git a/tools/crm_resource.c b/tools/crm_resource.c index db4ab8a7de..c72c171250 100644 --- a/tools/crm_resource.c +++ b/tools/crm_resource.c @@ -1,1663 +1,1666 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include gboolean do_force = FALSE; gboolean BE_QUIET = FALSE; const char *attr_set_type = XML_TAG_ATTR_SETS; char *host_id = NULL; const char *rsc_id = NULL; const char *host_uname = NULL; const char *prop_name = NULL; const char *prop_value = NULL; const char *rsc_type = NULL; const char *prop_id = NULL; const char *prop_set = NULL; char *move_lifetime = NULL; char rsc_cmd = 'L'; char *our_pid = NULL; IPC_Channel *crmd_channel = NULL; char *xml_file = NULL; int cib_options = cib_sync_call; int crmd_replies_needed = 0; GMainLoop *mainloop = NULL; extern void cleanup_alloc_calculations(pe_working_set_t *data_set); #define CMD_ERR(fmt, args...) do { \ crm_warn(fmt, ##args); \ fprintf(stderr, fmt, ##args); \ } while(0) #define message_timeout_ms 60*1000 static gboolean resource_ipc_timeout(gpointer data) { fprintf(stderr, "No messages received in %d seconds.. aborting\n", (int)message_timeout_ms/1000); crm_err("No messages received in %d seconds", (int)message_timeout_ms/1000); exit(-1); } static void resource_ipc_connection_destroy(gpointer user_data) { crm_info("Connection to CRMd was terminated"); exit(1); } static void start_mainloop(void) { mainloop = g_main_new(FALSE); crmd_replies_needed++; /* The welcome message */ fprintf(stderr, "Waiting for %d replies from the CRMd", crmd_replies_needed); crm_debug("Waiting for %d replies from the CRMd", crmd_replies_needed); g_timeout_add(message_timeout_ms, resource_ipc_timeout, NULL); g_main_run(mainloop); } static gboolean resource_ipc_callback(IPC_Channel * server, void *private_data) { int lpc = 0; xmlNode *msg = NULL; gboolean stay_connected = TRUE; while(IPC_ISRCONN(server)) { if(server->ops->is_message_pending(server) == 0) { break; } msg = xmlfromIPC(server, MAX_IPC_DELAY); if (msg == NULL) { break; } lpc++; fprintf(stderr, "."); crm_log_xml(LOG_DEBUG_2, "[inbound]", msg); crmd_replies_needed--; if(crmd_replies_needed == 0) { fprintf(stderr, " OK\n"); crm_debug("Got all the replies we expected"); crm_xml_cleanup(); exit(0); } free_xml(msg); msg = NULL; if(server->ch_status != IPC_CONNECT) { break; } } crm_debug_2("Processed %d messages (%d)", lpc, server->ch_status); if (server->ch_status != IPC_CONNECT) { stay_connected = FALSE; } return stay_connected; } static int do_find_resource(const char *rsc, pe_working_set_t *data_set) { int found = 0; GListPtr lpc = NULL; resource_t *the_rsc = pe_find_resource(data_set->resources, rsc); if(the_rsc == NULL) { return cib_NOTEXISTS; } for(lpc = the_rsc->running_on; lpc != NULL; lpc = lpc->next) { node_t *node = (node_t*)lpc->data; crm_debug_3("resource %s is running on: %s", rsc, node->details->uname); if(BE_QUIET) { fprintf(stdout, "%s\n", node->details->uname); } else { const char *state = ""; if(the_rsc->variant == pe_native && the_rsc->role == RSC_ROLE_MASTER) { state = "Master"; } fprintf(stdout, "resource %s is running on: %s %s\n", rsc, node->details->uname, state); } found++; } if(BE_QUIET == FALSE && found == 0) { fprintf(stderr, "resource %s is NOT running\n", rsc); } return 0; } #define cons_string(x) x?x:"NA" static void print_cts_constraints(pe_working_set_t *data_set) { + xmlNode *xml_obj = NULL; xmlNode *lifetime = NULL; - xmlNode * cib_constraints = get_object_root(XML_CIB_TAG_CONSTRAINTS, data_set->input); - xml_child_iter(cib_constraints, xml_obj, - - const char *id = crm_element_value(xml_obj, XML_ATTR_ID); - if(id == NULL) { - continue; - } + xmlNode *cib_constraints = get_object_root(XML_CIB_TAG_CONSTRAINTS, data_set->input); + for(xml_obj = cib_constraints; xml_obj != NULL; xml_obj = xml_obj->next) { + const char *id = crm_element_value(xml_obj, XML_ATTR_ID); + if(id == NULL) { + continue; + } - lifetime = first_named_child(xml_obj, "lifetime"); + lifetime = first_named_child(xml_obj, "lifetime"); - if(test_ruleset(lifetime, NULL, data_set->now) == FALSE) { - continue; - } + if(test_ruleset(lifetime, NULL, data_set->now) == FALSE) { + continue; + } - if(safe_str_eq(XML_CONS_TAG_RSC_DEPEND, crm_element_name(xml_obj))) { - printf("Constraint %s %s %s %s %s %s %s\n", - crm_element_name(xml_obj), - cons_string(crm_element_value(xml_obj, XML_ATTR_ID)), - cons_string(crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE)), - cons_string(crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET)), - cons_string(crm_element_value(xml_obj, XML_RULE_ATTR_SCORE)), - cons_string(crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE)), - cons_string(crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET_ROLE))); + if(safe_str_eq(XML_CONS_TAG_RSC_DEPEND, crm_element_name(xml_obj))) { + printf("Constraint %s %s %s %s %s %s %s\n", + crm_element_name(xml_obj), + cons_string(crm_element_value(xml_obj, XML_ATTR_ID)), + cons_string(crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE)), + cons_string(crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET)), + cons_string(crm_element_value(xml_obj, XML_RULE_ATTR_SCORE)), + cons_string(crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE)), + cons_string(crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET_ROLE))); - } else if(safe_str_eq(XML_CONS_TAG_RSC_LOCATION, crm_element_name(xml_obj))) { - /* unpack_rsc_location(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); */ + } + } } static void print_cts_rsc(resource_t *rsc) { GListPtr lpc = NULL; const char *host = NULL; gboolean needs_quorum = TRUE; const char *rtype = crm_element_value(rsc->xml, XML_ATTR_TYPE); const char *rprov = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER); const char *rclass = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); if(safe_str_eq(rclass, "stonith")) { + xmlNode *op = NULL; needs_quorum = FALSE; - } else { - xml_child_iter_filter(rsc->ops_xml, op, "op", - const char *name = crm_element_value(op, "name"); - if(safe_str_neq(name, CRMD_ACTION_START)) { - const char *value = crm_element_value(op, "requires"); - if(safe_str_eq(value, "nothing")) { - needs_quorum = FALSE; - } - break; - } - ); + for(op = rsc->ops_xml; op != NULL; op = op->next) { + if(crm_str_eq((const char *)op->name, "op", TRUE)) { + const char *name = crm_element_value(op, "name"); + if(safe_str_neq(name, CRMD_ACTION_START)) { + const char *value = crm_element_value(op, "requires"); + if(safe_str_eq(value, "nothing")) { + needs_quorum = FALSE; + } + break; + } + } + } } if(rsc->running_on != NULL && g_list_length(rsc->running_on) == 1) { node_t *tmp = rsc->running_on->data; host = tmp->details->uname; } printf("Resource: %s %s %s %s %s %s %s %s %d %lld 0x%.16llx\n", crm_element_name(rsc->xml), rsc->id, rsc->clone_name?rsc->clone_name:rsc->id, rsc->parent?rsc->parent->id:"NA", rprov?rprov:"NA", rclass, rtype, host?host:"NA", needs_quorum, rsc->flags, rsc->flags); for(lpc = rsc->children; lpc != NULL; lpc = lpc->next) { resource_t *child = (resource_t*)lpc->data; print_cts_rsc(child); } } static void print_raw_rsc(resource_t *rsc) { GListPtr lpc = NULL; GListPtr children = rsc->children; if(children == NULL) { printf("%s\n", rsc->id); } for(lpc = children; lpc != NULL; lpc = lpc->next) { resource_t *child = (resource_t*)lpc->data; print_raw_rsc(child); } } static int do_find_resource_list(pe_working_set_t *data_set, gboolean raw) { int found = 0; GListPtr lpc = NULL; for(lpc = data_set->resources; lpc != NULL; lpc = lpc->next) { resource_t *rsc = (resource_t*)lpc->data; if(is_set(rsc->flags, pe_rsc_orphan) && rsc->fns->active(rsc, TRUE) == FALSE) { continue; } rsc->fns->print( rsc, NULL, pe_print_printf|pe_print_rsconly, stdout); found++; } if(found == 0) { printf("NO resources configured\n"); return cib_NOTEXISTS; } return 0; } static resource_t *find_rsc_or_clone(const char *rsc, pe_working_set_t *data_set) { resource_t *the_rsc = pe_find_resource(data_set->resources, rsc); if(the_rsc == NULL) { char *as_clone = crm_concat(rsc, "0", ':'); the_rsc = pe_find_resource(data_set->resources, as_clone); crm_free(as_clone); } return the_rsc; } static int dump_resource(const char *rsc, pe_working_set_t *data_set) { char *rsc_xml = NULL; resource_t *the_rsc = find_rsc_or_clone(rsc, data_set); if(the_rsc == NULL) { return cib_NOTEXISTS; } the_rsc->fns->print(the_rsc, NULL, pe_print_printf, stdout); rsc_xml = dump_xml_formatted(the_rsc->xml); fprintf(stdout, "raw xml:\n%s\n", rsc_xml); crm_free(rsc_xml); return 0; } static int dump_resource_attr( const char *rsc, const char *attr, pe_working_set_t *data_set) { int rc = cib_NOTEXISTS; node_t *current = NULL; GHashTable *params = NULL; resource_t *the_rsc = find_rsc_or_clone(rsc, data_set); const char *value = NULL; if(the_rsc == NULL) { return cib_NOTEXISTS; } if(g_list_length(the_rsc->running_on) == 1) { current = the_rsc->running_on->data; } else if(g_list_length(the_rsc->running_on) > 1) { CMD_ERR("%s is active on more than one node," " returning the default value for %s\n", the_rsc->id, crm_str(value)); } params = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); if(safe_str_eq(attr_set_type, XML_TAG_ATTR_SETS)) { get_rsc_attributes(params, the_rsc, current, data_set); } else if(safe_str_eq(attr_set_type, XML_TAG_META_SETS)) { get_meta_attributes(params, the_rsc, current, data_set); } else { unpack_instance_attributes(data_set->input, the_rsc->xml, XML_TAG_UTILIZATION, NULL, params, NULL, FALSE, data_set->now); } crm_debug("Looking up %s in %s", attr, the_rsc->id); value = g_hash_table_lookup(params, attr); if(value != NULL) { fprintf(stdout, "%s\n", value); rc = 0; } g_hash_table_destroy(params); return rc; } static int find_resource_attr( cib_t *the_cib, const char *attr, const char *rsc, const char *set_type, const char *set_name, const char *attr_id, const char *attr_name, char **value) { int offset = 0; static int xpath_max = 1024; enum cib_errors rc = cib_ok; xmlNode *xml_search = NULL; char *xpath_string = NULL; CRM_ASSERT(value != NULL); *value = NULL; crm_malloc0(xpath_string, xpath_max); offset += snprintf(xpath_string + offset, xpath_max - offset, "%s", get_object_path("resources")); offset += snprintf(xpath_string + offset, xpath_max - offset, "//*[@id=\"%s\"]", rsc); if(set_type) { offset += snprintf(xpath_string + offset, xpath_max - offset, "//%s", set_type); if(set_name) { offset += snprintf(xpath_string + offset, xpath_max - offset, "[@id=\"%s\"]", set_name); } } offset += snprintf(xpath_string + offset, xpath_max - offset, "//nvpair["); if(attr_id) { offset += snprintf(xpath_string + offset, xpath_max - offset, "@id=\"%s\"", attr_id); } if(attr_name) { if(attr_id) { offset += snprintf(xpath_string + offset, xpath_max - offset, " and "); } offset += snprintf(xpath_string + offset, xpath_max - offset, "@name=\"%s\"", attr_name); } offset += snprintf(xpath_string + offset, xpath_max - offset, "]"); rc = the_cib->cmds->query( the_cib, xpath_string, &xml_search, cib_sync_call|cib_scope_local|cib_xpath); if(rc != cib_ok) { goto bail; } crm_log_xml_debug(xml_search, "Match"); if(xml_has_children(xml_search)) { + xmlNode *child = NULL; rc = cib_missing_data; printf("Multiple attributes match name=%s\n", attr_name); - xml_child_iter(xml_search, child, - printf(" Value: %s \t(id=%s)\n", - crm_element_value(child, XML_NVPAIR_ATTR_VALUE), ID(child)); - ); + for(child = xml_search; child != NULL; child = child->next) { + printf(" Value: %s \t(id=%s)\n", + crm_element_value(child, XML_NVPAIR_ATTR_VALUE), ID(child)); + } } else { const char *tmp = crm_element_value(xml_search, attr); if(tmp) { *value = crm_strdup(tmp); } } bail: crm_free(xpath_string); free_xml(xml_search); return rc; } static int set_resource_attr(const char *rsc_id, const char *attr_set, const char *attr_id, const char *attr_name, const char *attr_value, cib_t *cib, pe_working_set_t *data_set) { int rc = cib_ok; char *local_attr_id = NULL; char *local_attr_set = NULL; xmlNode *xml_top = NULL; xmlNode *xml_obj = NULL; gboolean use_attributes_tag = FALSE; resource_t *rsc = find_rsc_or_clone(rsc_id, data_set); if(rsc == NULL) { return cib_NOTEXISTS; } if(safe_str_eq(attr_set_type, XML_TAG_ATTR_SETS)) { rc = find_resource_attr( cib, XML_ATTR_ID, rsc_id, XML_TAG_META_SETS, attr_set, attr_id, attr_name, &local_attr_id); if(rc == cib_ok) { printf("WARNING: There is already a meta attribute called %s (id=%s)\n", attr_name, local_attr_id); } } rc = find_resource_attr( cib, XML_ATTR_ID, rsc_id, attr_set_type, attr_set, attr_id, attr_name, &local_attr_id); if(rc == cib_ok) { crm_debug("Found a match for name=%s: id=%s", attr_name, local_attr_id); attr_id = local_attr_id; } else if(rc != cib_NOTEXISTS) { crm_free(local_attr_id); return rc; } else { const char *value = NULL; xmlNode *cib_top = NULL; const char *tag = crm_element_name(rsc->xml); rc = cib->cmds->query(cib, "/cib", &cib_top, cib_sync_call|cib_scope_local|cib_xpath|cib_no_children); value = crm_element_value(cib_top, "ignore_dtd"); if(value != NULL) { use_attributes_tag = TRUE; } else { value = crm_element_value(cib_top, XML_ATTR_VALIDATION); if(value && strstr(value, "-0.6")) { use_attributes_tag = TRUE; } } free_xml(cib_top); if(attr_set == NULL) { local_attr_set = crm_concat(rsc_id, attr_set_type, '-'); attr_set = local_attr_set; } if(attr_id == NULL) { local_attr_id = crm_concat(attr_set, attr_name, '-'); attr_id = local_attr_id; } if(use_attributes_tag && safe_str_eq(tag, XML_CIB_TAG_MASTER)) { tag = "master_slave"; /* use the old name */ } xml_top = create_xml_node(NULL, tag); crm_xml_add(xml_top, XML_ATTR_ID, rsc_id); xml_obj = create_xml_node(xml_top, attr_set_type); crm_xml_add(xml_obj, XML_ATTR_ID, attr_set); if(use_attributes_tag) { xml_obj = create_xml_node(xml_obj, XML_TAG_ATTRS); } } xml_obj = create_xml_node(xml_obj, XML_CIB_TAG_NVPAIR); if(xml_top == NULL) { xml_top = xml_obj; } crm_xml_add(xml_obj, XML_ATTR_ID, attr_id); crm_xml_add(xml_obj, XML_NVPAIR_ATTR_NAME, attr_name); crm_xml_add(xml_obj, XML_NVPAIR_ATTR_VALUE, attr_value); crm_log_xml_debug(xml_top, "Update"); rc = cib->cmds->modify(cib, XML_CIB_TAG_RESOURCES, xml_top, cib_options); free_xml(xml_top); crm_free(local_attr_id); crm_free(local_attr_set); return rc; } static int delete_resource_attr( const char *rsc_id, const char *attr_set, const char *attr_id, const char *attr_name, cib_t *cib, pe_working_set_t *data_set) { xmlNode *xml_obj = NULL; int rc = cib_ok; char *local_attr_id = NULL; resource_t *rsc = find_rsc_or_clone(rsc_id, data_set); if(rsc == NULL) { return cib_NOTEXISTS; } rc = find_resource_attr( cib, XML_ATTR_ID, rsc_id, attr_set_type, attr_set, attr_id, attr_name, &local_attr_id); if(rc == cib_NOTEXISTS) { return cib_ok; } else if(rc != cib_ok) { return rc; } if(attr_id == NULL) { attr_id = local_attr_id; } xml_obj = create_xml_node(NULL, XML_CIB_TAG_NVPAIR); crm_xml_add(xml_obj, XML_ATTR_ID, attr_id); crm_xml_add(xml_obj, XML_NVPAIR_ATTR_NAME, attr_name); crm_log_xml_debug(xml_obj, "Delete"); rc = cib->cmds->delete(cib, XML_CIB_TAG_RESOURCES, xml_obj, cib_options); if(rc == cib_ok) { printf("Deleted %s option: id=%s%s%s%s%s\n", rsc_id, local_attr_id, attr_set?" set=":"", attr_set?attr_set:"", attr_name?" name=":"", attr_name?attr_name:""); } free_xml(xml_obj); crm_free(local_attr_id); return rc; } static int dump_resource_prop( const char *rsc, const char *attr, pe_working_set_t *data_set) { const char *value = NULL; resource_t *the_rsc = pe_find_resource(data_set->resources, rsc); if(the_rsc == NULL) { return cib_NOTEXISTS; } value = crm_element_value(the_rsc->xml, attr); if(value != NULL) { fprintf(stdout, "%s\n", value); return 0; } return cib_NOTEXISTS; } static int send_lrm_rsc_op(IPC_Channel *crmd_channel, const char *op, const char *host_uname, const char *rsc_id, gboolean only_failed, pe_working_set_t *data_set) { char *key = NULL; int rc = cib_send_failed; xmlNode *cmd = NULL; xmlNode *xml_rsc = NULL; const char *value = NULL; xmlNode *params = NULL; xmlNode *msg_data = NULL; resource_t *rsc = pe_find_resource(data_set->resources, rsc_id); if(rsc == NULL) { CMD_ERR("Resource %s not found\n", rsc_id); return cib_NOTEXISTS; } else if(rsc->variant != pe_native) { CMD_ERR("We can only process primitive resources, not %s\n", rsc_id); return cib_invalid_argument; } else if(host_uname == NULL) { CMD_ERR("Please supply a hostname with -H\n"); return cib_invalid_argument; } key = crm_concat("0:0:crm-resource", our_pid, '-'); msg_data = create_xml_node(NULL, XML_GRAPH_TAG_RSC_OP); crm_xml_add(msg_data, XML_ATTR_TRANSITION_KEY, key); crm_free(key); xml_rsc = create_xml_node(msg_data, XML_CIB_TAG_RESOURCE); if(rsc->clone_name) { crm_xml_add(xml_rsc, XML_ATTR_ID, rsc->clone_name); crm_xml_add(xml_rsc, XML_ATTR_ID_LONG, rsc->id); } else { crm_xml_add(xml_rsc, XML_ATTR_ID, rsc->id); crm_xml_add(xml_rsc, XML_ATTR_ID_LONG, rsc->long_name); } value = crm_element_value(rsc->xml, XML_ATTR_TYPE); crm_xml_add(xml_rsc, XML_ATTR_TYPE, value); if(value == NULL) { CMD_ERR("%s has no type! Aborting...\n", rsc_id); return cib_NOTEXISTS; } value = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); crm_xml_add(xml_rsc, XML_AGENT_ATTR_CLASS, value); if(value == NULL) { CMD_ERR("%s has no class! Aborting...\n", rsc_id); return cib_NOTEXISTS; } value = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER); crm_xml_add(xml_rsc, XML_AGENT_ATTR_PROVIDER, value); params = create_xml_node(msg_data, XML_TAG_ATTRS); crm_xml_add(params, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET); key = crm_meta_name(XML_LRM_ATTR_INTERVAL); crm_xml_add(params, key, "60000"); /* 1 minute */ crm_free(key); cmd = create_request(op, msg_data, host_uname, CRM_SYSTEM_CRMD, crm_system_name, our_pid); /* crm_log_xml_warn(cmd, "send_lrm_rsc_op"); */ free_xml(msg_data); if(send_ipc_message(crmd_channel, cmd)) { rc = 0; } else { CMD_ERR("Could not send %s op to the crmd", op); rc = cib_connection; } free_xml(cmd); return rc; } static int delete_lrm_rsc(IPC_Channel *crmd_channel, const char *host_uname, resource_t *rsc, pe_working_set_t *data_set) { int rc = cib_ok; if(rsc == NULL) { return cib_NOTEXISTS; } else if(rsc->children) { GListPtr lpc = NULL; for(lpc = rsc->children; lpc != NULL; lpc = lpc->next) { resource_t *child = (resource_t*)lpc->data; delete_lrm_rsc(crmd_channel, host_uname, child, data_set); } return cib_ok; } else if(host_uname == NULL) { GListPtr lpc = NULL; for(lpc = data_set->nodes; lpc != NULL; lpc = lpc->next) { node_t *node = (node_t*)lpc->data; if(node->details->online) { delete_lrm_rsc(crmd_channel, node->details->uname, rsc, data_set); } } return cib_ok; } printf("Cleaning up %s on %s\n", rsc->id, host_uname); rc = send_lrm_rsc_op(crmd_channel, CRM_OP_LRM_DELETE, host_uname, rsc->id, TRUE, data_set); if(rc == cib_ok) { char *attr_name = NULL; const char *id = rsc->id; crmd_replies_needed++; if(rsc->clone_name) { id = rsc->clone_name; } attr_name = crm_concat("fail-count", id, '-'); attrd_lazy_update('D', host_uname, attr_name, NULL, XML_CIB_TAG_STATUS, NULL, NULL); crm_free(attr_name); } return rc; } static int fail_lrm_rsc(IPC_Channel *crmd_channel, const char *host_uname, const char *rsc_id, pe_working_set_t *data_set) { crm_warn("Failing: %s", rsc_id); #if HAVE_STRUCT_LRM_OPS_FAIL_RSC crmd_replies_needed++; #endif return send_lrm_rsc_op(crmd_channel, CRM_OP_LRM_FAIL, host_uname, rsc_id, FALSE, data_set); } static int refresh_lrm(IPC_Channel *crmd_channel, const char *host_uname) { xmlNode *cmd = NULL; int rc = cib_send_failed; cmd = create_request(CRM_OP_LRM_REFRESH, NULL, host_uname, CRM_SYSTEM_CRMD, crm_system_name, our_pid); if(send_ipc_message(crmd_channel, cmd)) { rc = 0; } free_xml(cmd); return rc; } static int move_resource( const char *rsc_id, const char *existing_node, const char *preferred_node, cib_t * cib_conn) { char *later_s = NULL; enum cib_errors rc = cib_ok; char *id = NULL; xmlNode *rule = NULL; xmlNode *expr = NULL; xmlNode *constraints = NULL; xmlNode *fragment = NULL; xmlNode *can_run = NULL; xmlNode *dont_run = NULL; fragment = create_xml_node(NULL, XML_CIB_TAG_CONSTRAINTS); constraints = fragment; id = crm_concat("cli-prefer", rsc_id, '-'); can_run = create_xml_node(NULL, XML_CONS_TAG_RSC_LOCATION); crm_xml_add(can_run, XML_ATTR_ID, id); crm_free(id); id = crm_concat("cli-standby", rsc_id, '-'); dont_run = create_xml_node(NULL, XML_CONS_TAG_RSC_LOCATION); crm_xml_add(dont_run, XML_ATTR_ID, id); crm_free(id); if(move_lifetime) { char *life = crm_strdup(move_lifetime); char *life_mutable = life; ha_time_t *now = NULL; ha_time_t *later = NULL; ha_time_t *duration = parse_time_duration(&life_mutable); if(duration == NULL) { CMD_ERR("Invalid duration specified: %s\n", move_lifetime); CMD_ERR("Please refer to" " http://en.wikipedia.org/wiki/ISO_8601#Duration" " for examples of valid durations\n"); crm_free(life); return cib_invalid_argument; } now = new_ha_date(TRUE); later = add_time(now, duration); log_date(LOG_INFO, "now ", now, ha_log_date|ha_log_time); log_date(LOG_INFO, "later ", later, ha_log_date|ha_log_time); log_date(LOG_INFO, "duration", duration, ha_log_date|ha_log_time|ha_log_local); later_s = date_to_string(later, ha_log_date|ha_log_time); printf("Migration will take effect until: %s\n", later_s); free_ha_date(duration); free_ha_date(later); free_ha_date(now); crm_free(life); } if(existing_node == NULL) { crm_log_xml_notice(can_run, "Deleting"); rc = cib_conn->cmds->delete( cib_conn, XML_CIB_TAG_CONSTRAINTS, dont_run, cib_options); if(rc == cib_NOTEXISTS) { rc = cib_ok; } else if(rc != cib_ok) { goto bail; } } else { if(BE_QUIET == FALSE) { fprintf(stderr, "WARNING: Creating rsc_location constraint '%s'" " with a score of -INFINITY for resource %s" " on %s.\n", ID(dont_run), rsc_id, existing_node); CMD_ERR("\tThis will prevent %s from running" " on %s until the constraint is removed using" " the 'crm_resource -U' command or manually" " with cibadmin\n", rsc_id, existing_node); CMD_ERR("\tThis will be the case even if %s is" " the last node in the cluster\n", existing_node); CMD_ERR("\tThis message can be disabled with -Q\n"); } crm_xml_add(dont_run, "rsc", rsc_id); rule = create_xml_node(dont_run, XML_TAG_RULE); expr = create_xml_node(rule, XML_TAG_EXPRESSION); id = crm_concat("cli-standby-rule", rsc_id, '-'); crm_xml_add(rule, XML_ATTR_ID, id); crm_free(id); crm_xml_add(rule, XML_RULE_ATTR_SCORE, MINUS_INFINITY_S); crm_xml_add(rule, XML_RULE_ATTR_BOOLEAN_OP, "and"); id = crm_concat("cli-standby-expr", rsc_id, '-'); crm_xml_add(expr, XML_ATTR_ID, id); crm_free(id); crm_xml_add(expr, XML_EXPR_ATTR_ATTRIBUTE, "#uname"); crm_xml_add(expr, XML_EXPR_ATTR_OPERATION, "eq"); crm_xml_add(expr, XML_EXPR_ATTR_VALUE, existing_node); crm_xml_add(expr, XML_EXPR_ATTR_TYPE, "string"); if(later_s) { expr = create_xml_node(rule, "date_expression"); id = crm_concat("cli-standby-lifetime-end",rsc_id,'-'); crm_xml_add(expr, XML_ATTR_ID, id); crm_free(id); crm_xml_add(expr, "operation", "lt"); crm_xml_add(expr, "end", later_s); } add_node_copy(constraints, dont_run); } if(preferred_node == NULL) { crm_log_xml_notice(can_run, "Deleting"); rc = cib_conn->cmds->delete( cib_conn, XML_CIB_TAG_CONSTRAINTS, can_run, cib_options); if(rc == cib_NOTEXISTS) { rc = cib_ok; } else if(rc != cib_ok) { goto bail; } } else { crm_xml_add(can_run, "rsc", rsc_id); rule = create_xml_node(can_run, XML_TAG_RULE); expr = create_xml_node(rule, XML_TAG_EXPRESSION); id = crm_concat("cli-prefer-rule", rsc_id, '-'); crm_xml_add(rule, XML_ATTR_ID, id); crm_free(id); crm_xml_add(rule, XML_RULE_ATTR_SCORE, INFINITY_S); crm_xml_add(rule, XML_RULE_ATTR_BOOLEAN_OP, "and"); id = crm_concat("cli-prefer-expr", rsc_id, '-'); crm_xml_add(expr, XML_ATTR_ID, id); crm_free(id); crm_xml_add(expr, XML_EXPR_ATTR_ATTRIBUTE, "#uname"); crm_xml_add(expr, XML_EXPR_ATTR_OPERATION, "eq"); crm_xml_add(expr, XML_EXPR_ATTR_VALUE, preferred_node); crm_xml_add(expr, XML_EXPR_ATTR_TYPE, "string"); if(later_s) { expr = create_xml_node(rule, "date_expression"); id = crm_concat("cli-prefer-lifetime-end", rsc_id, '-'); crm_xml_add(expr, XML_ATTR_ID, id); crm_free(id); crm_xml_add(expr, "operation", "lt"); crm_xml_add(expr, "end", later_s); } add_node_copy(constraints, can_run); } if(preferred_node != NULL || existing_node != NULL) { crm_log_xml_notice(fragment, "CLI Update"); rc = cib_conn->cmds->update( cib_conn, XML_CIB_TAG_CONSTRAINTS, fragment, cib_options); } bail: free_xml(fragment); free_xml(dont_run); free_xml(can_run); crm_free(later_s); return rc; } static int list_resource_operations( const char *rsc_id, const char *host_uname, gboolean active, pe_working_set_t *data_set) { resource_t *rsc = NULL; int opts = pe_print_printf|pe_print_rsconly|pe_print_suppres_nl; GListPtr ops = find_operations(rsc_id, host_uname, active, data_set); GListPtr lpc = NULL; for(lpc = ops; lpc != NULL; lpc = lpc->next) { xmlNode *xml_op = (xmlNode*)lpc->data; const char *op_rsc = crm_element_value(xml_op, "resource"); const char *last = crm_element_value(xml_op, "last_run"); const char *status_s = crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS); int status = crm_parse_int(status_s, "0"); rsc = pe_find_resource(data_set->resources, op_rsc); rsc->fns->print(rsc, "", opts, stdout); fprintf(stdout, ": %s (node=%s, call=%s, rc=%s", ID(xml_op), crm_element_value(xml_op, XML_ATTR_UNAME), crm_element_value(xml_op, XML_LRM_ATTR_CALLID), crm_element_value(xml_op, XML_LRM_ATTR_RC)); if(last) { time_t run_at = crm_parse_int(last, "0"); fprintf(stdout, ", last-run=%s, exec=%sms\n", ctime(&run_at), crm_element_value(xml_op, "exec_time")); } fprintf(stdout, "): %s\n", op_status2text(status)); } return cib_ok; } #include "../pengine/pengine.h" static void show_location(resource_t *rsc, const char *prefix) { GListPtr lpc = NULL; GListPtr list = rsc->rsc_location; int offset = 0; if(prefix) { offset = strlen(prefix) - 2; } for(lpc = list; lpc != NULL; lpc = lpc->next) { rsc_to_node_t *cons = (rsc_to_node_t*)lpc->data; GListPtr lpc2 = NULL; for(lpc2 = cons->node_list_rh; lpc2 != NULL; lpc2 = lpc2->next) { node_t *node = (node_t*)lpc2->data; char *score = score2char(node->weight); fprintf(stdout, "%s: Node %-*s (score=%s, id=%s)\n", prefix?prefix:" ", 71-offset, node->details->uname, score, cons->id); crm_free(score); } } } static void show_colocation(resource_t *rsc, gboolean dependants, gboolean recursive, int offset) { char *prefix = NULL; GListPtr lpc = NULL; GListPtr list = rsc->rsc_cons; crm_malloc0(prefix, (offset*4) + 1); memset(prefix, ' ', offset*4); if(dependants) { list = rsc->rsc_cons_lhs; } if(is_set(rsc->flags, pe_rsc_allocating)) { /* Break colocation loops */ printf("loop %s\n", rsc->id); return; } set_bit(rsc->flags, pe_rsc_allocating); for(lpc = list; lpc != NULL; lpc = lpc->next) { rsc_colocation_t *cons = (rsc_colocation_t*)lpc->data; char *score = NULL; resource_t *peer = cons->rsc_rh; if(dependants) { peer = cons->rsc_lh; } if(is_set(peer->flags, pe_rsc_allocating)) { if(dependants == FALSE) { fprintf(stdout, "%s%-*s (id=%s - loop)\n", prefix, 80-(4*offset), peer->id, cons->id); } continue; } if(dependants && recursive) { show_colocation(peer, dependants, recursive, offset+1); } score = score2char(cons->score); if(cons->role_rh > RSC_ROLE_STARTED) { fprintf(stdout, "%s%-*s (score=%s, %s role=%s, id=%s)\n", prefix, 80-(4*offset), peer->id, score, dependants?"needs":"with", role2text(cons->role_rh), cons->id); } else { fprintf(stdout, "%s%-*s (score=%s, id=%s)\n", prefix, 80-(4*offset), peer->id, score, cons->id); } show_location(peer, prefix); crm_free(score); if(!dependants && recursive) { show_colocation(peer, dependants, recursive, offset+1); } } crm_free(prefix); } static struct crm_option long_options[] = { /* Top-level Options */ {"help", 0, 0, '?', "\t\tThis text"}, {"version", 0, 0, '$', "\t\tVersion information" }, {"verbose", 0, 0, 'V', "\t\tIncrease debug output"}, {"quiet", 0, 0, 'Q', "\t\tPrint only the value on stdout\n"}, {"resource", 1, 0, 'r', "\tResource ID" }, {"-spacer-",1, 0, '-', "\nQueries:"}, {"list", 0, 0, 'L', "\t\tList all resources"}, {"list-raw", 0, 0, 'l', "\tList the IDs of all instantiated resources (no groups/clones/...)"}, {"list-cts", 0, 0, 'c', NULL, 1}, {"list-operations", 0, 0, 'O', "\tList active resource operations. Optionally filtered by resource (-r) and/or node (-N)"}, {"list-all-operations", 0, 0, 'o', "List all resource operations. Optionally filtered by resource (-r) and/or node (-N)\n"}, {"query-xml", 0, 0, 'q', "\tQuery the definition of a resource"}, {"locate", 0, 0, 'W', "\t\tDisplay the current location(s) of a resource"}, {"stack", 0, 0, 'A', "\t\tDisplay the prerequisites and dependents of a resource"}, {"constraints",0, 0, 'a', "\tDisplay the (co)location constraints that apply to a resource"}, {"-spacer-", 1, 0, '-', "\nCommands:"}, {"set-parameter", 1, 0, 'p', "Set the named parameter for a resource. See also -m, --meta"}, {"get-parameter", 1, 0, 'g', "Display the named parameter for a resource. See also -m, --meta"}, {"delete-parameter",1, 0, 'd', "Delete the named parameter for a resource. See also -m, --meta"}, {"get-property", 1, 0, 'G', "Display the 'class', 'type' or 'provider' of a resource", 1}, {"set-property", 1, 0, 'S', "(Advanced) Set the class, type or provider of a resource", 1}, {"move", 0, 0, 'M', "\t\tMove a resource from its current location, optionally specifying a destination (-N) and/or a period for which it should take effect (-u)" "\n\t\t\t\tIf -N is not specified, the cluster will force the resource to move by creating a rule for the current location and a score of -INFINITY" "\n\t\t\t\tNOTE: This will prevent the resource from running on this node until the constraint is removed with -U"}, {"un-move", 0, 0, 'U', "\tRemove all constraints created by a move command"}, {"-spacer-", 1, 0, '-', "\nAdvanced Commands:"}, {"delete", 0, 0, 'D', "\t\tDelete a resource from the CIB"}, {"fail", 0, 0, 'F', "\t\tTell the cluster this resource has failed"}, {"refresh", 0, 0, 'R', "\t\t(Advanced) Refresh the CIB from the LRM"}, {"cleanup", 0, 0, 'C', "\t\t(Advanced) Delete a resource from the LRM"}, {"reprobe", 0, 0, 'P', "\t\t(Advanced) Re-check for resources started outside of the CRM\n"}, {"-spacer-", 1, 0, '-', "\nAdditional Options:"}, {"node", 1, 0, 'N', "\tHost uname"}, {"resource-type", 1, 0, 't', "Resource type (primitive, clone, group, ...)"}, {"parameter-value", 1, 0, 'v', "Value to use with -p, -g or -d"}, {"lifetime", 1, 0, 'u', "\tLifespan of migration constraints\n"}, {"meta", 0, 0, 'm', "\t\tModify a resource's configuration option rather than one which is passed to the resource agent script. For use with -p, -g, -d"}, {"utilization", 0, 0, 'z', "\tModify a resource's utilization attribute. For use with -p, -g, -d"}, {"set-name", 1, 0, 's', "\t(Advanced) ID of the instance_attributes object to change"}, {"nvpair", 1, 0, 'i', "\t(Advanced) ID of the nvpair object to change/delete"}, {"force", 0, 0, 'f', "\n" /* Is this actually true anymore? "\t\tForce the resource to move by creating a rule for the current location and a score of -INFINITY" "\n\t\tThis should be used if the resource's stickiness and constraint scores total more than INFINITY (Currently 100,000)" "\n\t\tNOTE: This will prevent the resource from running on this node until the constraint is removed with -U or the --lifetime duration expires\n"*/ }, {"xml-file", 1, 0, 'x', NULL, 1},\ /* legacy options */ {"host-uname", 1, 0, 'H', NULL, 1}, {"migrate", 0, 0, 'M', NULL, 1}, {"un-migrate", 0, 0, 'U', NULL, 1}, {"-spacer-", 1, 0, '-', "\nExamples:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', "List the configured resources:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_resource --list", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Display the current location of 'myResource':", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_resource --resource myResource --locate", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Move 'myResource' to another machine:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_resource --resource myResource --move", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Move 'myResource' to a specific machine:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_resource --resource myResource --move --node altNode", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Allow (but not force) 'myResource' to move back to its original location:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_resource --resource myResource --un-move", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Tell the cluster that 'myResource' failed:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_resource --resource myResource --fail", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Stop a 'myResource' (and anything that depends on it):", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_resource --resource myResource --set-parameter target-role --meta --parameter-value Stopped", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Tell the cluster not to manage 'myResource':", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', "The cluster will not attempt to start or stop the resource under any circumstances."}, {"-spacer-", 1, 0, '-', "Useful when performing maintenance tasks on a resource.", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_resource --resource myResource --set-parameter is-managed --meta --parameter-value false", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Erase the operation history of 'myResource' on 'aNode':", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', "The cluster will 'forget' the existing resource state (including any errors) and attempt to recover the resource."}, {"-spacer-", 1, 0, '-', "Useful when a resource had failed permanently and has been repaired by an administrator.", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_resource --resource myResource --cleanup --node aNode", pcmk_option_example}, {0, 0, 0, 0} }; int main(int argc, char **argv) { pe_working_set_t data_set; xmlNode *cib_xml_copy = NULL; cib_t * cib_conn = NULL; enum cib_errors rc = cib_ok; gboolean need_cib = TRUE; int option_index = 0; int argerr = 0; int flag; crm_log_init(NULL, LOG_ERR, FALSE, FALSE, argc, argv); crm_set_options("V?$LRQDCPp:WMUr:H:h:v:t:p:g:d:i:s:G:S:fx:lmzu:FOocqN:aA", "(query|command) [options]", long_options, "Perform tasks related to cluster resources.\n Allows resources to be queried (definition and location), modified, and moved around the cluster.\n"); if(argc < 2) { crm_help('?', LSB_EXIT_EINVAL); } while (1) { flag = crm_get_option(argc, argv, &option_index); if (flag == -1) break; switch(flag) { case 'V': cl_log_enable_stderr(TRUE); alter_debug(DEBUG_INC); break; case '$': case '?': crm_help(flag, LSB_EXIT_OK); break; case 'x': xml_file = crm_strdup(optarg); break; case 'Q': BE_QUIET = TRUE; break; case 'm': attr_set_type = XML_TAG_META_SETS; break; case 'z': attr_set_type = XML_TAG_UTILIZATION; break; case 'u': move_lifetime = crm_strdup(optarg); break; case 'f': do_force = TRUE; break; case 'i': prop_id = optarg; break; case 's': prop_set = optarg; break; case 'r': rsc_id = optarg; break; case 'v': prop_value = optarg; break; case 't': rsc_type = optarg; break; case 'R': case 'P': need_cib = FALSE; rsc_cmd = flag; break; case 'L': case 'c': case 'l': case 'q': case 'D': case 'F': case 'C': case 'W': case 'M': case 'U': case 'O': case 'o': case 'A': case 'a': rsc_cmd = flag; break; case 'p': case 'g': case 'd': case 'S': case 'G': prop_name = optarg; rsc_cmd = flag; break; case 'h': case 'H': case 'N': crm_debug_2("Option %c => %s", flag, optarg); host_uname = optarg; break; default: CMD_ERR("Argument code 0%o (%c) is not (?yet?) supported\n", flag, flag); ++argerr; break; } } if (optind < argc && argv[optind] != NULL) { CMD_ERR("non-option ARGV-elements: "); while (optind < argc && argv[optind] != NULL) { CMD_ERR("%s ", argv[optind++]); ++argerr; } CMD_ERR("\n"); } if (optind > argc) { ++argerr; } if (argerr) { crm_help('?', LSB_EXIT_GENERIC); } crm_malloc0(our_pid, 11); if(our_pid != NULL) { snprintf(our_pid, 10, "%d", getpid()); our_pid[10] = '\0'; } if(do_force) { crm_debug("Forcing..."); cib_options |= cib_scope_local|cib_quorum_override; } set_working_set_defaults(&data_set); if(need_cib) { resource_t *rsc = NULL; if(xml_file != NULL) { cib_xml_copy = filename2xml(xml_file); } else { cib_conn = cib_new(); rc = cib_conn->cmds->signon( cib_conn, crm_system_name, cib_command); if(rc != cib_ok) { CMD_ERR("Error signing on to the CIB service: %s\n", cib_error2string(rc)); return rc; } cib_xml_copy = get_cib_copy(cib_conn); } if(cli_config_update(&cib_xml_copy, NULL, FALSE) == FALSE) { rc = cib_STALE; goto bail; } data_set.input = cib_xml_copy; data_set.now = new_ha_date(TRUE); cluster_status(&data_set); if(rsc_id) { rsc = find_rsc_or_clone(rsc_id, &data_set); } if(rsc == NULL) { rc = cib_NOTEXISTS; } } if(rsc_cmd == 'R' || rsc_cmd == 'C' || rsc_cmd == 'F' || rsc_cmd == 'P') { GCHSource *src = NULL; src = init_client_ipc_comms(CRM_SYSTEM_CRMD, resource_ipc_callback, NULL, &crmd_channel); if(src == NULL) { CMD_ERR("Error signing on to the CRMd service\n"); rc = cib_connection; goto bail; } send_hello_message( crmd_channel, our_pid, crm_system_name, "0", "1"); set_IPC_Channel_dnotify(src, resource_ipc_connection_destroy); } if(rsc_cmd == 'L') { rc = cib_ok; do_find_resource_list(&data_set, FALSE); } else if(rsc_cmd == 'l') { int found = 0; GListPtr lpc = NULL; rc = cib_ok; for(lpc = data_set.resources; lpc != NULL; lpc = lpc->next) { resource_t *rsc = (resource_t*)lpc->data; found++; print_raw_rsc(rsc); } if(found == 0) { printf("NO resources configured\n"); rc = cib_NOTEXISTS; goto bail; } } else if(rsc_cmd == 'A' || rsc_cmd == 'a') { GListPtr lpc = NULL; resource_t *rsc = pe_find_resource(data_set.resources, rsc_id); xmlNode * cib_constraints = get_object_root(XML_CIB_TAG_CONSTRAINTS, data_set.input); if(rsc == NULL) { CMD_ERR("Must supply a resource id with -r\n"); rc = cib_NOTEXISTS; goto bail; } unpack_constraints(cib_constraints, &data_set); for(lpc = data_set.resources; lpc != NULL; lpc = lpc->next) { resource_t *r = (resource_t*)lpc->data; clear_bit(r->flags, pe_rsc_allocating); } show_colocation(rsc, TRUE, rsc_cmd=='A', 1); fprintf(stdout, "* %s\n", rsc->id); show_location(rsc, NULL); for(lpc = data_set.resources; lpc != NULL; lpc = lpc->next) { resource_t *r = (resource_t*)lpc->data; clear_bit(r->flags, pe_rsc_allocating); } show_colocation(rsc, FALSE, rsc_cmd=='A', 1); } else if(rsc_cmd == 'c') { int found = 0; GListPtr lpc = NULL; rc = cib_ok; for(lpc = data_set.resources; lpc != NULL; lpc = lpc->next) { resource_t *rsc = (resource_t*)lpc->data; print_cts_rsc(rsc); found++; } print_cts_constraints(&data_set); } else if(rsc_cmd == 'C') { resource_t *rsc = pe_find_resource(data_set.resources, rsc_id); rc = delete_lrm_rsc(crmd_channel, host_uname, rsc, &data_set); if(rc == cib_ok) { start_mainloop(); } } else if(rsc_cmd == 'F') { rc = fail_lrm_rsc(crmd_channel, host_uname, rsc_id, &data_set); if(rc == cib_ok) { start_mainloop(); } } else if(rsc_cmd == 'O') { rc = list_resource_operations(rsc_id, host_uname, TRUE, &data_set); } else if(rsc_cmd == 'o') { rc = list_resource_operations(rsc_id, host_uname, FALSE, &data_set); } else if(rc == cib_NOTEXISTS) { CMD_ERR("Resource %s not found: %s\n", crm_str(rsc_id), cib_error2string(rc)); } else if(rsc_cmd == 'W') { if(rsc_id == NULL) { CMD_ERR("Must supply a resource id with -r\n"); rc = cib_NOTEXISTS; goto bail; } rc = do_find_resource(rsc_id, &data_set); } else if(rsc_cmd == 'q') { if(rsc_id == NULL) { CMD_ERR("Must supply a resource id with -r\n"); rc = cib_NOTEXISTS; goto bail; } rc = dump_resource(rsc_id, &data_set); } else if(rsc_cmd == 'U') { if(rsc_id == NULL) { CMD_ERR("Must supply a resource id with -r\n"); rc = cib_NOTEXISTS; goto bail; } rc = move_resource(rsc_id, NULL, NULL, cib_conn); } else if(rsc_cmd == 'M') { node_t *dest = NULL; node_t *current = NULL; const char *current_uname = NULL; resource_t *rsc = pe_find_resource(data_set.resources, rsc_id); if(rsc != NULL && rsc->running_on != NULL) { current = rsc->running_on->data; if(current != NULL) { current_uname = current->details->uname; } } if(host_uname != NULL) { dest = pe_find_node(data_set.nodes, host_uname); } if(rsc == NULL) { CMD_ERR("Resource %s not moved:" " not found\n", rsc_id); } else if(rsc->variant == pe_native && g_list_length(rsc->running_on) > 1) { CMD_ERR("Resource %s not moved:" " active on multiple nodes\n", rsc_id); } else if(host_uname != NULL && dest == NULL) { CMD_ERR("Error performing operation: " "%s is not a known node\n", host_uname); rc = cib_NOTEXISTS; } else if(host_uname != NULL && safe_str_eq(current_uname, host_uname)) { CMD_ERR("Error performing operation: " "%s is already active on %s\n", rsc_id, host_uname); } else if(current_uname != NULL && (do_force || host_uname == NULL)) { rc = move_resource(rsc_id, current_uname, host_uname, cib_conn); } else if(host_uname != NULL) { rc = move_resource( rsc_id, NULL, host_uname, cib_conn); } else { CMD_ERR("Resource %s not moved: " "not-active and no preferred location" " specified.\n", rsc_id); rc = cib_missing; } } else if(rsc_cmd == 'G') { if(rsc_id == NULL) { CMD_ERR("Must supply a resource id with -r\n"); rc = cib_NOTEXISTS; goto bail; } rc = dump_resource_prop(rsc_id, prop_name, &data_set); } else if(rsc_cmd == 'S') { xmlNode *msg_data = NULL; if(prop_value == NULL || strlen(prop_value) == 0) { CMD_ERR("You need to supply a value with the -v option\n"); rc = CIBRES_MISSING_FIELD; goto bail; } else if(cib_conn == NULL) { rc = cib_connection; goto bail; } if(rsc_id == NULL) { CMD_ERR("Must supply a resource id with -r\n"); rc = cib_NOTEXISTS; goto bail; } CRM_LOG_ASSERT(rsc_type != NULL); CRM_LOG_ASSERT(prop_name != NULL); CRM_LOG_ASSERT(prop_value != NULL); msg_data = create_xml_node(NULL, rsc_type); crm_xml_add(msg_data, XML_ATTR_ID, rsc_id); crm_xml_add(msg_data, prop_name, prop_value); rc = cib_conn->cmds->modify( cib_conn, XML_CIB_TAG_RESOURCES, msg_data, cib_options); free_xml(msg_data); } else if(rsc_cmd == 'g') { if(rsc_id == NULL) { CMD_ERR("Must supply a resource id with -r\n"); rc = cib_NOTEXISTS; goto bail; } rc = dump_resource_attr(rsc_id, prop_name, &data_set); } else if(rsc_cmd == 'p') { if(rsc_id == NULL) { CMD_ERR("Must supply a resource id with -r\n"); rc = cib_NOTEXISTS; goto bail; } if(prop_value == NULL || strlen(prop_value) == 0) { CMD_ERR("You need to supply a value with the -v option\n"); rc = CIBRES_MISSING_FIELD; goto bail; } rc = set_resource_attr(rsc_id, prop_set, prop_id, prop_name, prop_value, cib_conn, &data_set); } else if(rsc_cmd == 'd') { if(rsc_id == NULL) { CMD_ERR("Must supply a resource id with -r\n"); rc = cib_NOTEXISTS; goto bail; } rc = delete_resource_attr(rsc_id, prop_set, prop_id, prop_name, cib_conn, &data_set); } else if(rsc_cmd == 'P') { xmlNode *cmd = NULL; cmd = create_request(CRM_OP_REPROBE, NULL, host_uname, CRM_SYSTEM_CRMD, crm_system_name, our_pid); if(send_ipc_message(crmd_channel, cmd)) { start_mainloop(); } free_xml(cmd); } else if(rsc_cmd == 'R') { rc = refresh_lrm(crmd_channel, host_uname); if(rc == cib_ok) { start_mainloop(); } } else if(rsc_cmd == 'D') { xmlNode *msg_data = NULL; if(rsc_id == NULL) { CMD_ERR("Must supply a resource id with -r\n"); rc = cib_NOTEXISTS; goto bail; } if(rsc_type == NULL) { CMD_ERR("You need to specify a resource type with -t"); rc = cib_NOTEXISTS; goto bail; } else if(cib_conn == NULL) { rc = cib_connection; goto bail; } msg_data = create_xml_node(NULL, rsc_type); crm_xml_add(msg_data, XML_ATTR_ID, rsc_id); rc = cib_conn->cmds->delete( cib_conn, XML_CIB_TAG_RESOURCES, msg_data, cib_options); free_xml(msg_data); } else { CMD_ERR("Unknown command: %c\n", rsc_cmd); } bail: if(cib_conn != NULL) { cleanup_alloc_calculations(&data_set); cib_conn->cmds->signoff(cib_conn); cib_delete(cib_conn); } crm_xml_cleanup(); if(rc == cib_no_quorum) { CMD_ERR("Error performing operation: %s\n", cib_error2string(rc)); CMD_ERR("Try using -f\n"); } else if(rc != cib_ok) { CMD_ERR("Error performing operation: %s\n", cib_error2string(rc)); } return rc; } diff --git a/tools/crm_verify.c b/tools/crm_verify.c index 812666c0de..fa92a06709 100644 --- a/tools/crm_verify.c +++ b/tools/crm_verify.c @@ -1,297 +1,302 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include gboolean USE_LIVE_CIB = FALSE; char *cib_save = NULL; void usage(const char *cmd, int exit_status); extern gboolean stage0(pe_working_set_t *data_set); extern void cleanup_alloc_calculations(pe_working_set_t *data_set); extern xmlNode * do_calculations( - pe_working_set_t *data_set, xmlNode *xml_input, ha_time_t *now); + pe_working_set_t *data_set, xmlNode *xml_input, ha_time_t *now); static struct crm_option long_options[] = { /* Top-level Options */ {"help", 0, 0, '?', "\tThis text"}, {"version", 0, 0, '$', "\tVersion information" }, {"verbose", 0, 0, 'V', "\tIncrease debug output\n"}, {"-spacer-", 1, 0, '-', "\nData sources:"}, {"live-check", 0, 0, 'L', "Check the configuration used by the running cluster\n"}, {"xml-file", 1, 0, 'x', "Check the configuration in the named file"}, {"xml-text", 1, 0, 'X', "Check the configuration in the supplied string"}, {"xml-pipe", 0, 0, 'p', "Check the configuration piped in via stdin"}, {"-spacer-", 1, 0, '-', "\nAdditional Options:"}, {"save-xml", 1, 0, 'S', "Save the verified XML to the named file. Most useful with -L"}, {"-spacer-", 1, 0, '-', "\nExamples:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', "Check the consistency of the configuration in the running cluster:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_verify --live-check", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Check the consistency of the configuration in a given file and produce verbose output:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_verify --xml-file file.xml --verbose", pcmk_option_example}, {F_CRM_DATA, 1, 0, 'X', NULL, 1}, /* legacy */ {0, 0, 0, 0} }; int main(int argc, char **argv) { - xmlNode *cib_object = NULL; - xmlNode *status = NULL; - int argerr = 0; - int flag; - int option_index = 0; + xmlNode *cib_object = NULL; + xmlNode *status = NULL; + int argerr = 0; + int flag; + int option_index = 0; - pe_working_set_t data_set; - cib_t * cib_conn = NULL; - int rc = cib_ok; + pe_working_set_t data_set; + cib_t * cib_conn = NULL; + int rc = cib_ok; - gboolean xml_stdin = FALSE; - const char *xml_tag = NULL; - const char *xml_file = NULL; - const char *xml_string = NULL; + gboolean xml_stdin = FALSE; + const char *xml_tag = NULL; + const char *xml_file = NULL; + const char *xml_string = NULL; - g_log_set_handler(NULL, - G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL - | G_LOG_LEVEL_WARNING | G_LOG_LEVEL_MESSAGE - | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG - | G_LOG_FLAG_RECURSION | G_LOG_FLAG_FATAL, - cl_glib_msg_handler, NULL); + g_log_set_handler(NULL, + G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL + | G_LOG_LEVEL_WARNING | G_LOG_LEVEL_MESSAGE + | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG + | G_LOG_FLAG_RECURSION | G_LOG_FLAG_FATAL, + cl_glib_msg_handler, NULL); - /* and for good measure... - this enum is a bit field (!) */ - g_log_set_always_fatal((GLogLevelFlags)0); /*value out of range*/ + /* and for good measure... - this enum is a bit field (!) */ + g_log_set_always_fatal((GLogLevelFlags)0); /*value out of range*/ - crm_log_init_quiet(NULL, LOG_ERR, FALSE, TRUE, argc, argv); - crm_set_options("V?$X:x:pLS:", "[modifiers] data_source", long_options, - "Check a (complete) confiuration for syntax and common conceptual errors." - "\n\nChecks the well-formedness of an XML configuration, its conformance to the configured DTD/schema and for the presence of common misconfigurations." - "\n\nIt reports two classes of problems, errors and warnings." - " Errors must be fixed before the cluster will work properly." - " However, it is left up to the administrator to decide if the warnings should also be fixed."); + crm_log_init_quiet(NULL, LOG_ERR, FALSE, TRUE, argc, argv); + crm_set_options("V?$X:x:pLS:", "[modifiers] data_source", long_options, + "Check a (complete) confiuration for syntax and common conceptual errors." + "\n\nChecks the well-formedness of an XML configuration, its conformance to the configured DTD/schema and for the presence of common misconfigurations." + "\n\nIt reports two classes of problems, errors and warnings." + " Errors must be fixed before the cluster will work properly." + " However, it is left up to the administrator to decide if the warnings should also be fixed."); - while (1) { - flag = crm_get_option(argc, argv, &option_index); - if (flag == -1) - break; + while (1) { + flag = crm_get_option(argc, argv, &option_index); + if (flag == -1) + break; - switch(flag) { + 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"); + case 0: + printf("option %s", long_options[option_index].name); + if (optarg) + printf(" with arg %s", optarg); + printf("\n"); - break; + break; #endif - case 'X': - crm_debug_2("Option %c => %s", flag, optarg); - xml_string = crm_strdup(optarg); - break; - case 'x': - crm_debug_2("Option %c => %s", flag, optarg); - xml_file = crm_strdup(optarg); - break; - case 'p': - xml_stdin = TRUE; - break; - case 'S': - cib_save = crm_strdup(optarg); - break; - case 'V': - alter_debug(DEBUG_INC); - break; - case 'L': - USE_LIVE_CIB = TRUE; - break; - case '$': - case '?': - crm_help(flag, LSB_EXIT_OK); - break; - default: - fprintf(stderr, "Option -%c is not yet supported\n", flag); - ++argerr; - break; - } + case 'X': + crm_debug_2("Option %c => %s", flag, optarg); + xml_string = crm_strdup(optarg); + break; + case 'x': + crm_debug_2("Option %c => %s", flag, optarg); + xml_file = crm_strdup(optarg); + break; + case 'p': + xml_stdin = TRUE; + break; + case 'S': + cib_save = crm_strdup(optarg); + break; + case 'V': + alter_debug(DEBUG_INC); + break; + case 'L': + USE_LIVE_CIB = TRUE; + break; + case '$': + case '?': + crm_help(flag, LSB_EXIT_OK); + break; + default: + fprintf(stderr, "Option -%c is not yet supported\n", flag); + ++argerr; + break; } + } - if (optind < argc) { - printf("non-option ARGV-elements: "); - while (optind < argc) { - printf("%s ", argv[optind++]); - } - printf("\n"); + if (optind < argc) { + printf("non-option ARGV-elements: "); + while (optind < argc) { + printf("%s ", argv[optind++]); } + printf("\n"); + } - if (optind > argc) { - ++argerr; - } + if (optind > argc) { + ++argerr; + } - if (argerr) { - crm_err("%d errors in option parsing", argerr); - crm_help(flag, LSB_EXIT_GENERIC); - } + if (argerr) { + crm_err("%d errors in option parsing", argerr); + crm_help(flag, LSB_EXIT_GENERIC); + } - crm_info("=#=#=#=#= Getting XML =#=#=#=#="); + crm_info("=#=#=#=#= Getting XML =#=#=#=#="); - if(USE_LIVE_CIB) { - cib_conn = cib_new(); - rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command); - } + if(USE_LIVE_CIB) { + cib_conn = cib_new(); + rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command); + } - if(USE_LIVE_CIB) { - if(rc == cib_ok) { - int options = cib_scope_local|cib_sync_call; - crm_info("Reading XML from: live cluster"); - rc = cib_conn->cmds->query( - cib_conn, NULL, &cib_object, options); - } + if(USE_LIVE_CIB) { + if(rc == cib_ok) { + int options = cib_scope_local|cib_sync_call; + crm_info("Reading XML from: live cluster"); + rc = cib_conn->cmds->query( + cib_conn, NULL, &cib_object, options); + } - if(rc != cib_ok) { - 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; - } + if(rc != cib_ok) { + 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) { - cib_object = filename2xml(xml_file); - if(cib_object == NULL) { - fprintf(stderr, - "Couldn't parse input file: %s\n", xml_file); - return 4; - } + } else if(xml_file != NULL) { + cib_object = filename2xml(xml_file); + if(cib_object == NULL) { + fprintf(stderr, + "Couldn't parse input file: %s\n", xml_file); + return 4; + } - } else if(xml_string != NULL) { - cib_object = string2xml(xml_string); - if(cib_object == NULL) { - fprintf(stderr, - "Couldn't parse input string: %s\n", xml_string); - return 4; - } - } else if(xml_stdin) { - cib_object = stdin2xml(); - if(cib_object == NULL) { - fprintf(stderr, "Couldn't parse input from STDIN.\n"); - return 4; - } - - } else { - fprintf(stderr, "No configuration source specified." - " Use --help for usage information.\n"); - return 5; + } else if(xml_string != NULL) { + cib_object = string2xml(xml_string); + if(cib_object == NULL) { + fprintf(stderr, + "Couldn't parse input string: %s\n", xml_string); + return 4; } - - xml_tag = crm_element_name(cib_object); - if(safe_str_neq(xml_tag, XML_TAG_CIB)) { - fprintf(stderr, "This tool can only check complete configurations (ie. those starting with ).\n"); - return 6; + } else if(xml_stdin) { + cib_object = stdin2xml(); + if(cib_object == NULL) { + fprintf(stderr, "Couldn't parse input from STDIN.\n"); + return 4; } + + } else { + fprintf(stderr, "No configuration source specified." + " Use --help for usage information.\n"); + return 5; + } + + xml_tag = crm_element_name(cib_object); + if(safe_str_neq(xml_tag, XML_TAG_CIB)) { + fprintf(stderr, "This tool can only check complete configurations (ie. those starting with ).\n"); + return 6; + } - if(cib_save != NULL) { - write_xml_file(cib_object, cib_save, FALSE); - } + if(cib_save != NULL) { + write_xml_file(cib_object, cib_save, FALSE); + } - status = get_object_root(XML_CIB_TAG_STATUS, cib_object); - if(status == NULL) { - create_xml_node(cib_object, XML_CIB_TAG_STATUS); - } + status = get_object_root(XML_CIB_TAG_STATUS, cib_object); + if(status == NULL) { + create_xml_node(cib_object, XML_CIB_TAG_STATUS); + } #if CRM_DEPRECATED_SINCE_2_0_4 - xml_child_iter_filter(status, node_state, XML_CIB_TAG_STATE, - xml_remove_prop(node_state, XML_CIB_TAG_LRM); - ); + { + xmlNode *node_state = NULL; + for(node_state = status; node_state != NULL; node_state = node_state->next) { + if(crm_str_eq((const char *)node_state->name, XML_CIB_TAG_STATE, TRUE)) { + xml_remove_prop(node_state, XML_CIB_TAG_LRM); + } + } + } #endif - if(validate_xml(cib_object, NULL, FALSE) == FALSE) { - crm_config_err("CIB did not pass DTD/schema validation"); - free_xml(cib_object); - cib_object = NULL; + if(validate_xml(cib_object, NULL, FALSE) == FALSE) { + crm_config_err("CIB did not pass DTD/schema validation"); + free_xml(cib_object); + cib_object = NULL; - } else if(cli_config_update(&cib_object, NULL, FALSE) == FALSE) { - crm_config_error = TRUE; - free_xml(cib_object); cib_object = NULL; - fprintf(stderr, "The cluster will NOT be able to use this configuration.\n"); - fprintf(stderr, "Please manually update the configuration to conform to the %s syntax.\n", - LATEST_SCHEMA_VERSION); - } + } else if(cli_config_update(&cib_object, NULL, FALSE) == FALSE) { + crm_config_error = TRUE; + free_xml(cib_object); cib_object = NULL; + fprintf(stderr, "The cluster will NOT be able to use this configuration.\n"); + fprintf(stderr, "Please manually update the configuration to conform to the %s syntax.\n", + LATEST_SCHEMA_VERSION); + } - set_working_set_defaults(&data_set); - if(cib_object == NULL) { - } else if(USE_LIVE_CIB) { - /* we will always have a status section and can do a full simulation */ - do_calculations(&data_set, cib_object, NULL); - cleanup_alloc_calculations(&data_set); + set_working_set_defaults(&data_set); + if(cib_object == NULL) { + } else if(USE_LIVE_CIB) { + /* we will always have a status section and can do a full simulation */ + do_calculations(&data_set, cib_object, NULL); + cleanup_alloc_calculations(&data_set); - } else { - data_set.now = new_ha_date(TRUE); - data_set.input = cib_object; - stage0(&data_set); - cleanup_alloc_calculations(&data_set); - } + } else { + data_set.now = new_ha_date(TRUE); + data_set.input = cib_object; + stage0(&data_set); + cleanup_alloc_calculations(&data_set); + } - if(crm_config_error) { - fprintf(stderr, "Errors found during check: config not valid\n"); - if(crm_log_level < LOG_WARNING) { - fprintf(stderr, " -V may provide more details\n"); - } - rc = 2; + if(crm_config_error) { + fprintf(stderr, "Errors found during check: config not valid\n"); + if(crm_log_level < LOG_WARNING) { + fprintf(stderr, " -V may provide more details\n"); + } + rc = 2; - } else if(crm_config_warning) { - fprintf(stderr, "Warnings found during check: config may not be valid\n"); - if(crm_log_level < LOG_WARNING) { - fprintf(stderr, " Use -V for more details\n"); - } - rc = 1; + } else if(crm_config_warning) { + fprintf(stderr, "Warnings found during check: config may not be valid\n"); + if(crm_log_level < LOG_WARNING) { + fprintf(stderr, " Use -V for more details\n"); } + rc = 1; + } - if(USE_LIVE_CIB) { - cib_conn->cmds->signoff(cib_conn); - cib_delete(cib_conn); - } + if(USE_LIVE_CIB) { + cib_conn->cmds->signoff(cib_conn); + cib_delete(cib_conn); + } - return rc; + return rc; } diff --git a/tools/crmadmin.c b/tools/crmadmin.c index 0ae6d814f3..a7af491ec5 100644 --- a/tools/crmadmin.c +++ b/tools/crmadmin.c @@ -1,597 +1,601 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include int message_timer_id = -1; int message_timeout_ms = 30*1000; GMainLoop *mainloop = NULL; IPC_Channel *crmd_channel = NULL; char *admin_uuid = NULL; void usage(const char *cmd, int exit_status); gboolean do_init(void); int do_work(void); void crmadmin_ipc_connection_destroy(gpointer user_data); gboolean admin_msg_callback(IPC_Channel * source_data, void *private_data); char *pluralSection(const char *a_section); xmlNode *handleCibMod(void); int do_find_node_list(xmlNode *xml_node); gboolean admin_message_timeout(gpointer data); gboolean is_node_online(xmlNode *node_state); enum debug { - debug_none, - debug_dec, - debug_inc + debug_none, + debug_dec, + debug_inc }; gboolean BE_VERBOSE = FALSE; int expected_responses = 1; gboolean BASH_EXPORT = FALSE; gboolean DO_HEALTH = FALSE; gboolean DO_RESET = FALSE; gboolean DO_RESOURCE = FALSE; gboolean DO_ELECT_DC = FALSE; gboolean DO_WHOIS_DC = FALSE; gboolean DO_NODE_LIST = FALSE; gboolean BE_SILENT = FALSE; gboolean DO_RESOURCE_LIST = FALSE; enum debug DO_DEBUG = debug_none; const char *crmd_operation = NULL; xmlNode *msg_options = NULL; const char *standby_on_off = "on"; const char *admin_verbose = XML_BOOLEAN_FALSE; char *id = NULL; char *disconnect = NULL; char *dest_node = NULL; char *rsc_name = NULL; char *crm_option = NULL; int operation_status = 0; const char *sys_to = NULL; static struct crm_option long_options[] = { /* Top-level Options */ {"help", 0, 0, '?', "\tThis text"}, {"version", 0, 0, '$', "\tVersion information" }, {"quiet", 0, 0, 'q', "\tDisplay only the essential query information"}, {"verbose", 0, 0, 'V', "\tIncrease debug output"}, {"-spacer-", 1, 0, '-', "\nCommands:"}, /* daemon options */ {"debug_inc", 1, 0, 'i', "Increase the crmd's debug level on the specified host"}, {"debug_dec", 1, 0, 'd', "Decrease the crmd's debug level on the specified host"}, {"status", 1, 0, 'S', "Display the status of the specified node." }, {"-spacer-", 1, 0, '-', "\n\tResult is the node's internal FSM state which can be useful for debugging\n"}, {"dc_lookup", 0, 0, 'D', "Display the uname of the node co-ordinating the cluster."}, {"-spacer-", 1, 0, '-', "\n\tThis is an internal detail and is rarely useful to administrators except when deciding on which node to examine the logs.\n"}, {"nodes", 0, 0, 'N', "\tDisplay the uname of all member nodes"}, {"election", 0, 0, 'E', "(Advanced) Start an election for the cluster co-ordinator"}, {"kill", 1, 0, 'K', "(Advanced) Shut down the crmd (not the rest of the clusterstack ) on the specified node"}, {"health", 0, 0, 'H', NULL, 1}, {"-spacer-", 1, 0, '-', "\nAdditional Options:"}, {XML_ATTR_TIMEOUT, 1, 0, 't', "Time (in milliseconds) to wait before declaring the operation failed"}, {"bash-export", 0, 0, 'B', "Create Bash export entries of the form 'export uname=uuid'\n"}, {"-spacer-", 1, 0, '-', "Notes:"}, {"-spacer-", 1, 0, '-', " The -i,-d,-K and -E commands are rarely used and may be removed in future versions."}, {0, 0, 0, 0} }; int main(int argc, char **argv) { - int option_index = 0; - int argerr = 0; - int flag; - - crm_log_init(NULL, LOG_ERR, FALSE, TRUE, argc, argv); - crm_set_options("V?$K:S:HE:Dd:i:Nqt:B", "command [options]", long_options, - "Development tool for performing some crmd-specific commands." - "\n Likely to be replaced by crm_node in the future" ); - if(argc < 2) { - crm_help('?', LSB_EXIT_EINVAL); - } + int option_index = 0; + int argerr = 0; + int flag; + + crm_log_init(NULL, LOG_ERR, FALSE, TRUE, argc, argv); + crm_set_options("V?$K:S:HE:Dd:i:Nqt:B", "command [options]", long_options, + "Development tool for performing some crmd-specific commands." + "\n Likely to be replaced by crm_node in the future" ); + if(argc < 2) { + crm_help('?', LSB_EXIT_EINVAL); + } - while (1) { - flag = crm_get_option(argc, argv, &option_index); - if (flag == -1) - break; - - switch(flag) { - case 'V': - BE_VERBOSE = TRUE; - admin_verbose = XML_BOOLEAN_TRUE; - cl_log_enable_stderr(TRUE); - alter_debug(DEBUG_INC); - break; - case 't': - message_timeout_ms = atoi(optarg); - if(message_timeout_ms < 1) { - message_timeout_ms = 30*1000; - } - break; - - case '$': - case '?': - crm_help(flag, LSB_EXIT_OK); - break; - case 'D': - DO_WHOIS_DC = TRUE; - break; - case 'B': - BASH_EXPORT = TRUE; - break; - case 'K': - DO_RESET = TRUE; - crm_debug_2("Option %c => %s", flag, optarg); - dest_node = crm_strdup(optarg); - crmd_operation = CRM_OP_LOCAL_SHUTDOWN; - break; - case 'q': - BE_SILENT = TRUE; - break; - case 'i': - DO_DEBUG = debug_inc; - crm_debug_2("Option %c => %s", flag, optarg); - dest_node = crm_strdup(optarg); - break; - case 'd': - DO_DEBUG = debug_dec; - crm_debug_2("Option %c => %s", flag, optarg); - dest_node = crm_strdup(optarg); - break; - case 'S': - DO_HEALTH = TRUE; - crm_debug_2("Option %c => %s", flag, optarg); - dest_node = crm_strdup(optarg); - break; - case 'E': - DO_ELECT_DC = TRUE; - break; - case 'N': - DO_NODE_LIST = TRUE; - break; - case 'H': - DO_HEALTH = TRUE; - break; - default: - printf("Argument code 0%o (%c) is not (?yet?) supported\n", flag, flag); - ++argerr; - break; + while (1) { + flag = crm_get_option(argc, argv, &option_index); + if (flag == -1) + break; + + switch(flag) { + case 'V': + BE_VERBOSE = TRUE; + admin_verbose = XML_BOOLEAN_TRUE; + cl_log_enable_stderr(TRUE); + alter_debug(DEBUG_INC); + break; + case 't': + message_timeout_ms = atoi(optarg); + if(message_timeout_ms < 1) { + message_timeout_ms = 30*1000; } + break; + + case '$': + case '?': + crm_help(flag, LSB_EXIT_OK); + break; + case 'D': + DO_WHOIS_DC = TRUE; + break; + case 'B': + BASH_EXPORT = TRUE; + break; + case 'K': + DO_RESET = TRUE; + crm_debug_2("Option %c => %s", flag, optarg); + dest_node = crm_strdup(optarg); + crmd_operation = CRM_OP_LOCAL_SHUTDOWN; + break; + case 'q': + BE_SILENT = TRUE; + break; + case 'i': + DO_DEBUG = debug_inc; + crm_debug_2("Option %c => %s", flag, optarg); + dest_node = crm_strdup(optarg); + break; + case 'd': + DO_DEBUG = debug_dec; + crm_debug_2("Option %c => %s", flag, optarg); + dest_node = crm_strdup(optarg); + break; + case 'S': + DO_HEALTH = TRUE; + crm_debug_2("Option %c => %s", flag, optarg); + dest_node = crm_strdup(optarg); + break; + case 'E': + DO_ELECT_DC = TRUE; + break; + case 'N': + DO_NODE_LIST = TRUE; + break; + case 'H': + DO_HEALTH = TRUE; + break; + default: + printf("Argument code 0%o (%c) is not (?yet?) supported\n", flag, flag); + ++argerr; + break; } + } - if (optind < argc) { - printf("non-option ARGV-elements: "); - while (optind < argc) - printf("%s ", argv[optind++]); - printf("\n"); - } + if (optind < argc) { + printf("non-option ARGV-elements: "); + while (optind < argc) + printf("%s ", argv[optind++]); + printf("\n"); + } - if (optind > argc) { - ++argerr; - } + if (optind > argc) { + ++argerr; + } - if (argerr) { - crm_help('?', LSB_EXIT_GENERIC); - } + if (argerr) { + crm_help('?', LSB_EXIT_GENERIC); + } - if (do_init()) { - int res = 0; - res = do_work(); - if (res > 0) { - /* wait for the reply by creating a mainloop and running it until - * the callbacks are invoked... - */ - mainloop = g_main_new(FALSE); - expected_responses++; - crm_debug_2("Waiting for %d replies from the local CRM", expected_responses); - - message_timer_id = g_timeout_add( - message_timeout_ms, admin_message_timeout, NULL); + if (do_init()) { + int res = 0; + res = do_work(); + if (res > 0) { + /* wait for the reply by creating a mainloop and running it until + * the callbacks are invoked... + */ + mainloop = g_main_new(FALSE); + expected_responses++; + crm_debug_2("Waiting for %d replies from the local CRM", expected_responses); + + message_timer_id = g_timeout_add( + message_timeout_ms, admin_message_timeout, NULL); - g_main_run(mainloop); + g_main_run(mainloop); - } else if(res < 0) { - crm_err("No message to send"); - operation_status = -1; - } - } else { - crm_warn("Init failed, could not perform requested operations"); - operation_status = -2; + } else if(res < 0) { + crm_err("No message to send"); + operation_status = -1; } + } else { + crm_warn("Init failed, could not perform requested operations"); + operation_status = -2; + } - crm_debug_2("%s exiting normally", crm_system_name); - return operation_status; + crm_debug_2("%s exiting normally", crm_system_name); + return operation_status; } int do_work(void) { - int ret = 1; - /* construct the request */ - xmlNode *msg_data = NULL; - gboolean all_is_good = TRUE; + int ret = 1; + /* construct the request */ + xmlNode *msg_data = NULL; + gboolean all_is_good = TRUE; - msg_options = create_xml_node(NULL, XML_TAG_OPTIONS); - crm_xml_add(msg_options, XML_ATTR_VERBOSE, admin_verbose); - crm_xml_add(msg_options, XML_ATTR_TIMEOUT, "0"); + msg_options = create_xml_node(NULL, XML_TAG_OPTIONS); + crm_xml_add(msg_options, XML_ATTR_VERBOSE, admin_verbose); + crm_xml_add(msg_options, XML_ATTR_TIMEOUT, "0"); - if (DO_HEALTH == TRUE) { - crm_debug_2("Querying the system"); + if (DO_HEALTH == TRUE) { + crm_debug_2("Querying the system"); - sys_to = CRM_SYSTEM_DC; + sys_to = CRM_SYSTEM_DC; - if (dest_node != NULL) { - sys_to = CRM_SYSTEM_CRMD; - crmd_operation = CRM_OP_PING; + if (dest_node != NULL) { + sys_to = CRM_SYSTEM_CRMD; + crmd_operation = CRM_OP_PING; - if (BE_VERBOSE) { - expected_responses = 1; - } + if (BE_VERBOSE) { + expected_responses = 1; + } - crm_xml_add(msg_options, XML_ATTR_TIMEOUT, "0"); + crm_xml_add(msg_options, XML_ATTR_TIMEOUT, "0"); - } else { - crm_info("Cluster-wide health not available yet"); - all_is_good = FALSE; - } + } else { + crm_info("Cluster-wide health not available yet"); + all_is_good = FALSE; + } - } else if(DO_ELECT_DC) { - /* tell the local node to initiate an election */ + } else if(DO_ELECT_DC) { + /* tell the local node to initiate an election */ - sys_to = CRM_SYSTEM_CRMD; - crmd_operation = CRM_OP_VOTE; + sys_to = CRM_SYSTEM_CRMD; + crmd_operation = CRM_OP_VOTE; - crm_xml_add(msg_options, XML_ATTR_TIMEOUT, "0"); + crm_xml_add(msg_options, XML_ATTR_TIMEOUT, "0"); - dest_node = NULL; + dest_node = NULL; - ret = 0; /* no return message */ + ret = 0; /* no return message */ - } else if(DO_WHOIS_DC) { - sys_to = CRM_SYSTEM_DC; - crmd_operation = CRM_OP_PING; + } else if(DO_WHOIS_DC) { + sys_to = CRM_SYSTEM_DC; + crmd_operation = CRM_OP_PING; - crm_xml_add(msg_options, XML_ATTR_TIMEOUT, "0"); + crm_xml_add(msg_options, XML_ATTR_TIMEOUT, "0"); - dest_node = NULL; + dest_node = NULL; - } else if(DO_NODE_LIST) { + } else if(DO_NODE_LIST) { - cib_t * the_cib = cib_new(); - xmlNode *output = NULL; + cib_t * the_cib = cib_new(); + xmlNode *output = NULL; - enum cib_errors rc = the_cib->cmds->signon( - the_cib, crm_system_name, cib_command); + enum cib_errors rc = the_cib->cmds->signon( + the_cib, crm_system_name, cib_command); - if(rc != cib_ok) { - return -1; - } + if(rc != cib_ok) { + return -1; + } - output = get_cib_copy(the_cib); - do_find_node_list(output); + output = get_cib_copy(the_cib); + do_find_node_list(output); - free_xml(output); - the_cib->cmds->signoff(the_cib); - exit(rc); + free_xml(output); + the_cib->cmds->signoff(the_cib); + exit(rc); - } else if(DO_RESET) { - /* tell dest_node to initiate the shutdown proceedure - * - * if dest_node is NULL, the request will be sent to the - * local node - */ - sys_to = CRM_SYSTEM_CRMD; - crm_xml_add(msg_options, XML_ATTR_TIMEOUT, "0"); + } else if(DO_RESET) { + /* tell dest_node to initiate the shutdown proceedure + * + * if dest_node is NULL, the request will be sent to the + * local node + */ + sys_to = CRM_SYSTEM_CRMD; + crm_xml_add(msg_options, XML_ATTR_TIMEOUT, "0"); - ret = 0; /* no return message */ + ret = 0; /* no return message */ - } else if(DO_DEBUG == debug_inc) { - /* tell dest_node to increase its debug level - * - * if dest_node is NULL, the request will be sent to the - * local node - */ - sys_to = CRM_SYSTEM_CRMD; - crmd_operation = CRM_OP_DEBUG_UP; + } else if(DO_DEBUG == debug_inc) { + /* tell dest_node to increase its debug level + * + * if dest_node is NULL, the request will be sent to the + * local node + */ + sys_to = CRM_SYSTEM_CRMD; + crmd_operation = CRM_OP_DEBUG_UP; - ret = 0; /* no return message */ + ret = 0; /* no return message */ - } else if(DO_DEBUG == debug_dec) { - /* tell dest_node to increase its debug level - * - * if dest_node is NULL, the request will be sent to the - * local node - */ - sys_to = CRM_SYSTEM_CRMD; - crmd_operation = CRM_OP_DEBUG_DOWN; + } else if(DO_DEBUG == debug_dec) { + /* tell dest_node to increase its debug level + * + * if dest_node is NULL, the request will be sent to the + * local node + */ + sys_to = CRM_SYSTEM_CRMD; + crmd_operation = CRM_OP_DEBUG_DOWN; - ret = 0; /* no return message */ + ret = 0; /* no return message */ - } else { - crm_err("Unknown options"); - all_is_good = FALSE; - } + } else { + crm_err("Unknown options"); + all_is_good = FALSE; + } - if(all_is_good == FALSE) { - crm_err("Creation of request failed. No message to send"); - return -1; - } + if(all_is_good == FALSE) { + crm_err("Creation of request failed. No message to send"); + return -1; + } /* send it */ - if (crmd_channel == NULL) { - crm_err("The IPC connection is not valid, cannot send anything"); - return -1; - } + if (crmd_channel == NULL) { + crm_err("The IPC connection is not valid, cannot send anything"); + return -1; + } - if(sys_to == NULL) { - if (dest_node != NULL) { - sys_to = CRM_SYSTEM_CRMD; - } else { - sys_to = CRM_SYSTEM_DC; - } + if(sys_to == NULL) { + if (dest_node != NULL) { + sys_to = CRM_SYSTEM_CRMD; + } else { + sys_to = CRM_SYSTEM_DC; } + } - { - xmlNode *cmd = create_request( - crmd_operation, msg_data, dest_node, sys_to, - crm_system_name, admin_uuid); + { + xmlNode *cmd = create_request( + crmd_operation, msg_data, dest_node, sys_to, + crm_system_name, admin_uuid); - send_ipc_message(crmd_channel, cmd); - free_xml(cmd); - } + send_ipc_message(crmd_channel, cmd); + free_xml(cmd); + } - return ret; + return ret; } void crmadmin_ipc_connection_destroy(gpointer user_data) { crm_err("Connection to CRMd was terminated"); if(mainloop) { g_main_quit(mainloop); } else { exit(1); } } gboolean do_init(void) { - GCHSource *src = NULL; + GCHSource *src = NULL; - crm_malloc0(admin_uuid, 11); - if(admin_uuid != NULL) { - snprintf(admin_uuid, 10, "%d", getpid()); - admin_uuid[10] = '\0'; - } + crm_malloc0(admin_uuid, 11); + if(admin_uuid != NULL) { + snprintf(admin_uuid, 10, "%d", getpid()); + admin_uuid[10] = '\0'; + } - src = init_client_ipc_comms( - CRM_SYSTEM_CRMD, admin_msg_callback, NULL, &crmd_channel); + src = init_client_ipc_comms( + CRM_SYSTEM_CRMD, admin_msg_callback, NULL, &crmd_channel); - if(DO_RESOURCE || DO_RESOURCE_LIST || DO_NODE_LIST) { - return TRUE; + if(DO_RESOURCE || DO_RESOURCE_LIST || DO_NODE_LIST) { + return TRUE; - } else if(crmd_channel != NULL) { - send_hello_message( - crmd_channel, admin_uuid, crm_system_name,"0", "1"); + } else if(crmd_channel != NULL) { + send_hello_message( + crmd_channel, admin_uuid, crm_system_name,"0", "1"); - set_IPC_Channel_dnotify(src, crmadmin_ipc_connection_destroy); + set_IPC_Channel_dnotify(src, crmadmin_ipc_connection_destroy); - return TRUE; - } - return FALSE; + return TRUE; + } + return FALSE; } gboolean admin_msg_callback(IPC_Channel * server, void *private_data) { - int rc = 0; - int lpc = 0; - xmlNode *xml = NULL; - IPC_Message *msg = NULL; - gboolean hack_return_good = TRUE; - static int received_responses = 0; - const char *result = NULL; - - g_source_remove(message_timer_id); - - while (server->ch_status != IPC_DISCONNECT - && server->ops->is_message_pending(server) == TRUE) { - rc = server->ops->recv(server, &msg); - if (rc != IPC_OK) { - crm_perror(LOG_ERR,"Receive failure (%d)", rc); - return !hack_return_good; - } + int rc = 0; + int lpc = 0; + xmlNode *xml = NULL; + IPC_Message *msg = NULL; + gboolean hack_return_good = TRUE; + static int received_responses = 0; + const char *result = NULL; + + g_source_remove(message_timer_id); + + while (server->ch_status != IPC_DISCONNECT + && server->ops->is_message_pending(server) == TRUE) { + rc = server->ops->recv(server, &msg); + if (rc != IPC_OK) { + crm_perror(LOG_ERR,"Receive failure (%d)", rc); + return !hack_return_good; + } - if (msg == NULL) { - crm_debug_4("No message this time"); - continue; - } + if (msg == NULL) { + crm_debug_4("No message this time"); + continue; + } - lpc++; - received_responses++; + lpc++; + received_responses++; - xml = convert_ipc_message(msg, __FUNCTION__); - msg->msg_done(msg); - crm_log_xml(LOG_MSG, "ipc", xml); + xml = convert_ipc_message(msg, __FUNCTION__); + msg->msg_done(msg); + crm_log_xml(LOG_MSG, "ipc", xml); - if (xml == NULL) { - crm_info("XML in IPC message was not valid... " - "discarding."); - goto cleanup; + if (xml == NULL) { + crm_info("XML in IPC message was not valid... " + "discarding."); + goto cleanup; - } else if (validate_crm_message( - xml, crm_system_name, admin_uuid, - XML_ATTR_RESPONSE) == FALSE) { - crm_debug_2("Message was not a CRM response. Discarding."); - goto cleanup; - } + } else if (validate_crm_message( + xml, crm_system_name, admin_uuid, + XML_ATTR_RESPONSE) == FALSE) { + crm_debug_2("Message was not a CRM response. Discarding."); + goto cleanup; + } - result = crm_element_value(xml, XML_ATTR_RESULT); - if(result == NULL || strcasecmp(result, "ok") == 0) { - result = "pass"; - } else { - result = "fail"; - } + result = crm_element_value(xml, XML_ATTR_RESULT); + if(result == NULL || strcasecmp(result, "ok") == 0) { + result = "pass"; + } else { + result = "fail"; + } - if(DO_HEALTH) { - xmlNode *data = get_message_xml(xml, F_CRM_DATA); - const char *state = crm_element_value(data, "crmd_state"); - - printf("Status of %s@%s: %s (%s)\n", - crm_element_value(data,XML_PING_ATTR_SYSFROM), - crm_element_value(xml, F_CRM_HOST_FROM), - state, - crm_element_value(data,XML_PING_ATTR_STATUS)); + if(DO_HEALTH) { + xmlNode *data = get_message_xml(xml, F_CRM_DATA); + const char *state = crm_element_value(data, "crmd_state"); + + printf("Status of %s@%s: %s (%s)\n", + crm_element_value(data,XML_PING_ATTR_SYSFROM), + crm_element_value(xml, F_CRM_HOST_FROM), + state, + crm_element_value(data,XML_PING_ATTR_STATUS)); - if(BE_SILENT && state != NULL) { - fprintf(stderr, "%s\n", state); - } + if(BE_SILENT && state != NULL) { + fprintf(stderr, "%s\n", state); + } - } else if(DO_WHOIS_DC) { - const char *dc = crm_element_value(xml, F_CRM_HOST_FROM); + } else if(DO_WHOIS_DC) { + const char *dc = crm_element_value(xml, F_CRM_HOST_FROM); - printf("Designated Controller is: %s\n", dc); - if(BE_SILENT && dc != NULL) { - fprintf(stderr, "%s\n", dc); - } - } - - cleanup: - free_xml(xml); - xml = NULL; + printf("Designated Controller is: %s\n", dc); + if(BE_SILENT && dc != NULL) { + fprintf(stderr, "%s\n", dc); + } } + + cleanup: + free_xml(xml); + xml = NULL; + } - if (server->ch_status == IPC_DISCONNECT) { - crm_debug_2("admin_msg_callback: received HUP"); - return !hack_return_good; - } + if (server->ch_status == IPC_DISCONNECT) { + crm_debug_2("admin_msg_callback: received HUP"); + return !hack_return_good; + } - if (received_responses >= expected_responses) { - crm_debug_2( - "Received expected number (%d) of messages from Heartbeat." - " Exiting normally.", expected_responses); - exit(0); - } + if (received_responses >= expected_responses) { + crm_debug_2( + "Received expected number (%d) of messages from Heartbeat." + " Exiting normally.", expected_responses); + exit(0); + } - message_timer_id = g_timeout_add( - message_timeout_ms, admin_message_timeout, NULL); + message_timer_id = g_timeout_add( + message_timeout_ms, admin_message_timeout, NULL); - return hack_return_good; + return hack_return_good; } gboolean admin_message_timeout(gpointer data) { - fprintf(stderr, "No messages received in %d seconds.. aborting\n", - (int)message_timeout_ms/1000); - crm_err("No messages received in %d seconds", - (int)message_timeout_ms/1000); - operation_status = -3; - g_main_quit(mainloop); - return FALSE; + fprintf(stderr, "No messages received in %d seconds.. aborting\n", + (int)message_timeout_ms/1000); + crm_err("No messages received in %d seconds", + (int)message_timeout_ms/1000); + operation_status = -3; + g_main_quit(mainloop); + return FALSE; } gboolean is_node_online(xmlNode *node_state) { - const char *uname = crm_element_value(node_state,XML_ATTR_UNAME); - const char *join_state = crm_element_value(node_state,XML_CIB_ATTR_JOINSTATE); - const char *exp_state = crm_element_value(node_state,XML_CIB_ATTR_EXPSTATE); - const char *crm_state = crm_element_value(node_state,XML_CIB_ATTR_CRMDSTATE); - const char *ha_state = crm_element_value(node_state,XML_CIB_ATTR_HASTATE); - const char *ccm_state = crm_element_value(node_state,XML_CIB_ATTR_INCCM); - - if(safe_str_neq(join_state, CRMD_JOINSTATE_DOWN) - && (ha_state == NULL || safe_str_eq(ha_state, "active")) - && crm_is_true(ccm_state) - && safe_str_eq(crm_state, "online")) { - crm_debug_3("Node %s is online", uname); - return TRUE; - } - crm_debug_3("Node %s: ha=%s ccm=%s join=%s exp=%s crm=%s", - uname, crm_str(ha_state), crm_str(ccm_state), - crm_str(join_state), crm_str(exp_state), - crm_str(crm_state)); - crm_debug_3("Node %s is offline", uname); - return FALSE; + const char *uname = crm_element_value(node_state,XML_ATTR_UNAME); + const char *join_state = crm_element_value(node_state,XML_CIB_ATTR_JOINSTATE); + const char *exp_state = crm_element_value(node_state,XML_CIB_ATTR_EXPSTATE); + const char *crm_state = crm_element_value(node_state,XML_CIB_ATTR_CRMDSTATE); + const char *ha_state = crm_element_value(node_state,XML_CIB_ATTR_HASTATE); + const char *ccm_state = crm_element_value(node_state,XML_CIB_ATTR_INCCM); + + if(safe_str_neq(join_state, CRMD_JOINSTATE_DOWN) + && (ha_state == NULL || safe_str_eq(ha_state, "active")) + && crm_is_true(ccm_state) + && safe_str_eq(crm_state, "online")) { + crm_debug_3("Node %s is online", uname); + return TRUE; + } + crm_debug_3("Node %s: ha=%s ccm=%s join=%s exp=%s crm=%s", + uname, crm_str(ha_state), crm_str(ccm_state), + crm_str(join_state), crm_str(exp_state), + crm_str(crm_state)); + crm_debug_3("Node %s is offline", uname); + return FALSE; } int do_find_node_list(xmlNode *xml_node) { - int found = 0; - xmlNode *nodes = get_object_root(XML_CIB_TAG_NODES, xml_node); - - xml_child_iter_filter( - nodes, node, XML_CIB_TAG_NODE, - if(BASH_EXPORT) { - printf("export %s=%s\n", - crm_element_value(node, XML_ATTR_UNAME), - crm_element_value(node, XML_ATTR_ID)); - } else { - printf("%s node: %s (%s)\n", - crm_element_value(node, XML_ATTR_TYPE), - crm_element_value(node, XML_ATTR_UNAME), - crm_element_value(node, XML_ATTR_ID)); - } - found++; - ); - if(found == 0) { - printf("NO nodes configured\n"); + int found = 0; + xmlNode *node = NULL; + xmlNode *nodes = get_object_root(XML_CIB_TAG_NODES, xml_node); + + for(node = nodes; node != NULL; node = node->next) { + if(crm_str_eq((const char *)node->name, XML_CIB_TAG_NODE, TRUE)) { + + if(BASH_EXPORT) { + printf("export %s=%s\n", + crm_element_value(node, XML_ATTR_UNAME), + crm_element_value(node, XML_ATTR_ID)); + } else { + printf("%s node: %s (%s)\n", + crm_element_value(node, XML_ATTR_TYPE), + crm_element_value(node, XML_ATTR_UNAME), + crm_element_value(node, XML_ATTR_ID)); + } + found++; } + } + + if(found == 0) { + printf("NO nodes configured\n"); + } - return found; + return found; }