diff --git a/crm/admin/cibadmin.c b/crm/admin/cibadmin.c index 4d076062e3..483ed049e6 100644 --- a/crm/admin/cibadmin.c +++ b/crm/admin/cibadmin.c @@ -1,585 +1,585 @@ -/* $Id: cibadmin.c,v 1.13 2004/12/09 14:45:00 andrew Exp $ */ +/* $Id: cibadmin.c,v 1.14 2004/12/16 14:34:17 andrew Exp $ */ /* * 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 /* someone complaining about _ha_msg_mod not being found */ #include int exit_code = cib_ok; int message_timer_id = -1; int message_timeout_ms = 30*1000; GMainLoop *mainloop = NULL; const char *crm_system_name = "cibadmin"; 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(const char *xml_text, int command_options, xmlNodePtr *output); gboolean admin_msg_callback(IPC_Channel * source_data, void *private_data); xmlNodePtr handleCibMod(const char *xml); gboolean admin_message_timeout(gpointer data); void cib_connection_destroy(gpointer user_data); void cibadmin_op_callback( const struct ha_msg *msg, int call_id, int rc, xmlNodePtr output); int command_options = 0; const char *cib_action = NULL; typedef struct str_list_s { int num_items; char *value; struct str_list_s *next; } str_list_t; char *id = NULL; char *this_msg_reference = NULL; char *obj_type = NULL; char *clear = 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; const char *sys_to = NULL; cib_t *the_cib = NULL; #define OPTARGS "V?i:o:QDUCEX:t:Srwlsh:MB" int main(int argc, char **argv) { int option_index = 0; int argerr = 0; int flag; int level = 0; char *xml_text = NULL; xmlNodePtr output = NULL; static struct option long_options[] = { /* Top-level Options */ {CRM_OP_CIB_ERASE, 0, 0, 'E'}, {CRM_OP_CIB_QUERY, 0, 0, 'Q'}, {CRM_OP_CIB_CREATE, 0, 0, 'C'}, {CRM_OP_CIB_REPLACE, 0, 0, 'R'}, {CRM_OP_CIB_UPDATE, 0, 0, 'U'}, {CRM_OP_CIB_DELETE, 0, 0, 'D'}, {CRM_OP_CIB_BUMP, 0, 0, 'B'}, {CRM_OP_CIB_SYNC, 0, 0, 'S'}, {CRM_OP_CIB_SLAVE, 0, 0, 'r'}, {CRM_OP_CIB_MASTER, 0, 0, 'w'}, {CRM_OP_CIB_ISMASTER,0, 0, 'M'}, {"local", 0, 0, 'l'}, {"sync-call", 0, 0, 's'}, {"host", 0, 0, 'h'}, {"xml", 1, 0, 'X'}, {"verbose", 0, 0, 'V'}, {"help", 0, 0, '?'}, {"reference", 1, 0, 0}, {"timeout", 1, 0, 't'}, /* common options */ {XML_ATTR_ID, 1, 0, 'i'}, {"obj_type", 1, 0, 'o'}, {0, 0, 0, 0} }; if(argc < 2) { usage(crm_system_name, LSB_EXIT_EINVAL); } /* Redirect messages from glib functions to our handler */ 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... */ g_log_set_always_fatal((GLogLevelFlags)0); cl_log_set_entity(crm_system_name); cl_log_set_facility(LOG_USER); while (1) { flag = getopt_long(argc, argv, OPTARGS, long_options, &option_index); if (flag == -1) break; switch(flag) { case 0: printf("option %s", long_options[option_index].name); if (optarg) printf(" with arg %s", optarg); printf("\n"); if (safe_str_eq("reference", long_options[option_index].name)) { this_msg_reference = crm_strdup(optarg); } else { printf("Long option (--%s) is not (yet?) properly supported\n", long_options[option_index].name); ++argerr; } break; case 't': message_timeout_ms = atoi(optarg); if(message_timeout_ms < 1) { message_timeout_ms = 30*1000; } break; case 'E': cib_action = crm_strdup(CRM_OP_CIB_ERASE); break; case 'Q': cib_action = crm_strdup(CRM_OP_CIB_QUERY); break; case 'S': cib_action = crm_strdup(CRM_OP_CIB_SYNC); break; case 'U': cib_action = crm_strdup(CRM_OP_CIB_UPDATE); break; case 'R': cib_action = crm_strdup(CRM_OP_CIB_REPLACE); break; case 'C': cib_action = crm_strdup(CRM_OP_CIB_CREATE); break; case 'D': cib_action = crm_strdup(CRM_OP_CIB_DELETE); break; case 'M': cib_action = crm_strdup(CRM_OP_CIB_ISMASTER); command_options |= cib_scope_local; break; case 'B': cib_action = crm_strdup(CRM_OP_CIB_BUMP); break; case 'r': cib_action = crm_strdup(CRM_OP_CIB_SLAVE); break; case 'w': cib_action = crm_strdup(CRM_OP_CIB_MASTER); command_options |= cib_scope_local; break; case 'V': level = get_crm_log_level(); command_options = command_options | cib_verbose; cl_log_enable_stderr(TRUE); set_crm_log_level(level+1); break; case '?': usage(crm_system_name, LSB_EXIT_OK); break; case 'i': crm_verbose("Option %c => %s", flag, optarg); id = crm_strdup(optarg); break; case 'o': crm_verbose("Option %c => %s", flag, optarg); obj_type = crm_strdup(optarg); break; case 'X': xml_text = crm_strdup(optarg); break; case 'h': host = crm_strdup(optarg); break; case 'l': command_options |= cib_scope_local; break; case 's': command_options |= cib_sync_call; 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) { ++argerr; } if(cib_action == NULL) { usage(crm_system_name, cib_operation); } if (argerr) { usage(crm_system_name, LSB_EXIT_GENERIC); } 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(xml_text, command_options, &output); if (exit_code > 0) { /* wait for the reply by creating a mainloop and running it until * the callbacks are invoked... */ IPC_Channel *ch = the_cib->cmds->channel(the_cib); request_id = exit_code; if(ch == NULL) { crm_err("Connection to CIB is corrupt"); return 2; } mainloop = g_main_new(FALSE); crm_debug("Setting operation timeout to %dms", message_timeout_ms); message_timer_id = Gmain_timeout_add( message_timeout_ms, admin_message_timeout, NULL); crm_debug("%s waiting for reply from the local CIB", crm_system_name); 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", cib_error2string(exit_code)); + fprintf(stderr, "Call failed: %s\n", + cib_error2string(exit_code)); operation_status = exit_code; - } if(output != NULL) { char *buffer = dump_xml_formatted(output); fprintf(stdout, "%s", crm_str(buffer)); crm_free(buffer); } crm_debug("%s exiting normally", crm_system_name); return -exit_code; } xmlNodePtr handleCibMod(const char *xml) { const char *attr_name = NULL; const char *attr_value = NULL; xmlNodePtr fragment = NULL; xmlNodePtr cib_object = NULL; if(xml == NULL) { cib_object = file2xml(stdin); } else { cib_object = string2xml(xml); } if(cib_object == NULL) { return NULL; } attr_name = XML_ATTR_ID; attr_value = xmlGetProp(cib_object, attr_name); if(attr_name == NULL || strlen(attr_name) == 0) { crm_err("No value for %s specified.", attr_name); return NULL; } crm_trace("Object creation complete"); /* create the cib request */ fragment = create_cib_fragment(cib_object, NULL); return fragment; } int do_work(const char *xml_text, int call_options, xmlNodePtr *output) { /* construct the request */ xmlNodePtr msg_data = NULL; char *obj_type_parent = NULL; obj_type_parent = cib_pluralSection(obj_type); if(strcmp(CRM_OP_CIB_QUERY, cib_action) == 0) { crm_verbose("Querying the CIB for section: %s", obj_type_parent); return the_cib->cmds->query_from( the_cib, host, obj_type_parent, output, call_options); } else if (strcmp(CRM_OP_CIB_ERASE, cib_action) == 0) { crm_trace("CIB Erase op in progress"); return the_cib->cmds->erase(the_cib, output, call_options); } else if (strcmp(CRM_OP_CIB_CREATE, cib_action) == 0) { enum cib_errors rc = cib_ok; crm_trace("Performing %s op...", cib_action); msg_data = handleCibMod(xml_text); rc = the_cib->cmds->create( the_cib, obj_type_parent, msg_data, output, call_options); free_xml(msg_data); return rc; } else if (strcmp(CRM_OP_CIB_UPDATE, cib_action) == 0) { enum cib_errors rc = cib_ok; crm_trace("Performing %s op...", cib_action); msg_data = handleCibMod(xml_text); rc = the_cib->cmds->modify( the_cib, obj_type_parent, msg_data, output, call_options); free_xml(msg_data); return rc; } else if (strcmp(CRM_OP_CIB_DELETE, cib_action) == 0) { enum cib_errors rc = cib_ok; crm_trace("Performing %s op...", cib_action); msg_data = handleCibMod(xml_text); rc = the_cib->cmds->delete( the_cib, obj_type_parent, msg_data, output, call_options); free_xml(msg_data); return rc; } else if (strcmp(CRM_OP_CIB_SYNC, cib_action) == 0) { crm_trace("Performing %s op...", cib_action); return the_cib->cmds->sync_from( the_cib, host, obj_type_parent, call_options); } else if (strcmp(CRM_OP_CIB_SLAVE, cib_action) == 0 && (call_options ^ cib_scope_local) ) { crm_trace("Performing %s op on all nodes...", cib_action); return the_cib->cmds->set_slave_all(the_cib, call_options); } else if (strcmp(CRM_OP_CIB_MASTER, cib_action) == 0) { crm_trace("Performing %s op on all nodes...", cib_action); return the_cib->cmds->set_master(the_cib, call_options); } else if(cib_action != NULL) { crm_trace("Passing \"%s\" to variant_op...", cib_action); return the_cib->cmds->variant_op( the_cib, cib_action, host, obj_type_parent, NULL, output, call_options); } else { crm_err("You must specify an operation"); } return cib_operation; } enum cib_errors do_init(void) { enum cib_errors rc = cib_ok; /* docs say only do this once, but in their code they do it every time! */ xmlInitParser(); the_cib = cib_new(); rc = the_cib->cmds->signon(the_cib, 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)); } else { rc = the_cib->cmds->set_op_callback( the_cib, cibadmin_op_callback); if(rc != cib_ok) { crm_err("Failed to set callback: %s", cib_error2string(rc)); fprintf(stderr,"Failed to set callback: %s\n", cib_error2string(rc)); } } return rc; } void usage(const char *cmd, int exit_status) { FILE *stream; stream = exit_status != 0 ? stderr : stdout; fprintf(stream, "usage: %s [-?Vio] command\n" "\twhere necessary, XML data will be expected using -X" " or on STDIN if -X isnt specified\n", cmd); fprintf(stream, "Options\n"); fprintf(stream, "\t--%s (-%c) \tid of the object being operated on\n", XML_ATTR_ID, 'i'); fprintf(stream, "\t--%s (-%c) \tobject type being operated on\n", "obj_type", 'o'); fprintf(stream, "\t--%s (-%c)\tturn on debug info." " additional instance increase verbosity\n", "verbose", 'V'); fprintf(stream, "\t--%s (-%c)\tthis help message\n", "help", '?'); fprintf(stream, "\nCommands\n"); fprintf(stream, "\t--%s (-%c)\t\n", CRM_OP_CIB_ERASE, 'E'); fprintf(stream, "\t--%s (-%c)\t\n", CRM_OP_CIB_QUERY, 'Q'); fprintf(stream, "\t--%s (-%c)\t\n", CRM_OP_CIB_CREATE, 'C'); fprintf(stream, "\t--%s (-%c)\t\n", CRM_OP_CIB_REPLACE,'R'); fprintf(stream, "\t--%s (-%c)\t\n", CRM_OP_CIB_UPDATE, 'U'); fprintf(stream, "\t--%s (-%c)\t\n", CRM_OP_CIB_DELETE, 'D'); fprintf(stream, "\t--%s (-%c)\t\n", CRM_OP_CIB_BUMP, 'B'); fprintf(stream, "\t--%s (-%c)\t\n", CRM_OP_CIB_ISMASTER,'M'); fprintf(stream, "\t--%s (-%c)\t\n", CRM_OP_CIB_SYNC, 'S'); fprintf(stream, "\nXML data\n"); fprintf(stream, "\t--%s (-%c) \t\n", "xml", 'X'); fprintf(stream, "\nAdvanced Options\n"); fprintf(stream, "\t--%s (-%c)\tsend command to specified host." " Applies to %s and %s commands only\n", "host", 'h', CRM_OP_CIB_QUERY, CRM_OP_CIB_SYNC); fprintf(stream, "\t--%s (-%c)\tcommand only takes effect locally" " on the specified host\n", "local", 'l'); fprintf(stream, "\t--%s (-%c)\twait for call to complete before" " returning\n", "sync-call", 's'); fflush(stream); exit(exit_status); } gboolean admin_message_timeout(gpointer data) { if(safe_str_eq(cib_action, CRM_OP_CIB_SLAVE)) { exit_code = cib_ok; fprintf(stdout, "CIB service(s) are in slave mode.\n"); } else { exit_code = cib_reply_failed; 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); } g_main_quit(mainloop); return FALSE; } void cib_connection_destroy(gpointer user_data) { crm_err("Connection to the CIB terminated... exiting"); g_main_quit(mainloop); return; } void cibadmin_op_callback( const struct ha_msg *msg, int call_id, int rc, xmlNodePtr output) { char *xml_text = NULL; crm_info("our callback was invoked"); cl_log_message(msg); exit_code = rc; xml_text = dump_xml_formatted(output); if(safe_str_eq(cib_action, CRM_OP_CIB_ISMASTER) && rc == cib_not_master) { crm_info("Local CIB is _not_ the master instance\n"); fprintf(stderr, "Local CIB is _not_ the master instance\n"); } else if(safe_str_eq(cib_action, CRM_OP_CIB_ISMASTER) && rc == cib_ok) { crm_info("Local CIB _is_ the master instance\n"); fprintf(stderr, "Local CIB _is_ the master instance\n"); } else if(rc != 0) { - crm_warn("Call %s failed (%d): %s", + crm_warn("Call %s failed (%d): %s\n", cib_action, rc, cib_error2string(rc)); fprintf(stderr, "Call %s failed (%d): %s\n", cib_action, rc, cib_error2string(rc)); fprintf(stdout, "%s\n", xml_text); } else if(output == NULL) { crm_info("Call passed"); } else { crm_info("Call passed"); fprintf(stdout, "%s\n", xml_text); } crm_free(xml_text); 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); } } diff --git a/crm/cib/callbacks.c b/crm/cib/callbacks.c index 2037f4f59c..ba41102288 100644 --- a/crm/cib/callbacks.c +++ b/crm/cib/callbacks.c @@ -1,788 +1,928 @@ -/* $Id: callbacks.c,v 1.5 2004/12/14 14:43:02 andrew Exp $ */ +/* $Id: callbacks.c,v 1.6 2004/12/16 14:34:18 andrew Exp $ */ /* * 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 +gint cib_GCompareFunc(gconstpointer a, gconstpointer b); +gboolean cib_msg_timeout(gpointer data); +void cib_GHFunc(gpointer key, gpointer value, gpointer user_data); + int next_client_id = 0; gboolean cib_is_master = FALSE; GHashTable *client_list = NULL; extern const char *cib_our_uname; extern ll_cluster_t *hb_conn; /* technically bump does modify the cib... * but we want to split the "bump" from the "sync" */ cib_operation_t cib_server_ops[] = { {NULL, FALSE, FALSE, FALSE, FALSE, cib_process_default}, {CRM_OP_NOOP, FALSE, FALSE, FALSE, FALSE, cib_process_default}, {CRM_OP_RETRIVE_CIB, FALSE, FALSE, FALSE, FALSE, cib_process_query}, {CRM_OP_CIB_SLAVE, FALSE, TRUE, FALSE, FALSE, cib_process_readwrite}, {CRM_OP_CIB_SLAVEALL,TRUE, TRUE, FALSE, FALSE, cib_process_readwrite}, {CRM_OP_CIB_MASTER, FALSE, TRUE, FALSE, FALSE, cib_process_readwrite}, {CRM_OP_CIB_ISMASTER,FALSE, TRUE, FALSE, FALSE, cib_process_readwrite}, {CRM_OP_CIB_BUMP, FALSE, TRUE, TRUE, FALSE, cib_process_bump}, {CRM_OP_CIB_REPLACE, TRUE, TRUE, TRUE, TRUE, cib_process_replace}, {CRM_OP_CIB_CREATE, TRUE, TRUE, TRUE, TRUE, cib_process_modify}, {CRM_OP_CIB_UPDATE, TRUE, TRUE, TRUE, TRUE, cib_process_modify}, {CRM_OP_JOINACK, TRUE, TRUE, TRUE, TRUE, cib_process_modify}, {CRM_OP_SHUTDOWN_REQ,TRUE, TRUE, TRUE, TRUE, cib_process_modify}, {CRM_OP_CIB_DELETE, TRUE, TRUE, TRUE, TRUE, cib_process_modify}, {CRM_OP_CIB_QUERY, FALSE, FALSE, TRUE, FALSE, cib_process_query}, {CRM_OP_QUIT, FALSE, TRUE, FALSE, FALSE, cib_process_quit}, {CRM_OP_PING, FALSE, FALSE, FALSE, FALSE, cib_process_ping}, {CRM_OP_CIB_ERASE, TRUE, TRUE, TRUE, FALSE, cib_process_erase} }; int send_via_callback_channel(struct ha_msg *msg, const char *token); enum cib_errors cib_process_command( const struct ha_msg *request, struct ha_msg **reply, gboolean privileged); gboolean cib_common_callback( IPC_Channel *channel, gpointer user_data, gboolean privileged); enum cib_errors cib_get_operation_id(const struct ha_msg* msg, int *operation); gboolean cib_process_disconnect(IPC_Channel *channel, cib_client_t *cib_client); gboolean cib_client_connect(IPC_Channel *channel, gpointer user_data) { gboolean auth_failed = FALSE; gboolean can_connect = TRUE; gboolean (*client_callback)(IPC_Channel *channel, gpointer user_data) = NULL; cib_client_t *new_client = NULL; crm_debug("Connecting channel"); if (channel == NULL) { crm_err("Channel was NULL"); can_connect = FALSE; } else if (channel->ch_status == IPC_DISCONNECT) { crm_err("Channel was disconnected"); can_connect = FALSE; } else if(user_data == NULL) { crm_err("user_data must contain channel name"); can_connect = FALSE; } else { - crm_malloc(new_client, sizeof(crmd_client_t)); + crm_malloc(new_client, sizeof(cib_client_t)); new_client->id = NULL; new_client->callback_id = NULL; new_client->source = NULL; new_client->channel = channel; new_client->channel_name = user_data; + new_client->delegated_calls = NULL; + crm_debug("Created channel %p for channel %s", + new_client, new_client->channel_name); + client_callback = NULL; /* choose callback and do auth based on channel_name */ if(safe_str_eq(new_client->channel_name, "cib_callback")) { client_callback = cib_null_callback; } else { uuid_t client_id; uuid_generate(client_id); crm_malloc(new_client->id, sizeof(char)*36); uuid_unparse(client_id, new_client->id); new_client->id[35] = EOS; uuid_generate(client_id); crm_malloc(new_client->callback_id, sizeof(char)*30); uuid_unparse(client_id, new_client->callback_id); new_client->callback_id[35] = EOS; client_callback = cib_ro_callback; if(safe_str_eq(new_client->channel_name, "cib_rw")) { client_callback = cib_rw_callback; } } } if(auth_failed) { crm_err("Connection to %s channel failed authentication", (char *)user_data); can_connect = FALSE; } if(can_connect == FALSE) { if(new_client) { crm_free(new_client->id); crm_free(new_client->callback_id); } crm_free(new_client); return FALSE; } channel->ops->set_recv_qlen(channel, 100); channel->ops->set_send_qlen(channel, 100); if(client_callback != NULL) { new_client->source = G_main_add_IPC_Channel( G_PRIORITY_LOW, channel, FALSE, client_callback, new_client, default_ipc_connection_destroy); } if(client_callback != cib_null_callback) { /* send msg to client with uuid to use when signing up for * callback channel */ struct ha_msg *reg_msg = ha_msg_new(3); ha_msg_add(reg_msg, F_CIB_OPERATION, CRM_OP_REGISTER); ha_msg_add(reg_msg, F_CIB_CLIENTID, new_client->id); ha_msg_add( reg_msg, F_CIB_CALLBACK_TOKEN, new_client->callback_id); msg2ipcchan(reg_msg, channel); ha_msg_del(reg_msg); /* make sure we can find ourselves later for sync calls * redirected to the master instance */ g_hash_table_insert(client_list, new_client->id, new_client); } crm_info("Channel %s connected for client %s", new_client->channel_name, new_client->id); return TRUE; } gboolean cib_rw_callback(IPC_Channel *channel, gpointer user_data) { return cib_common_callback(channel, user_data, TRUE); } gboolean cib_ro_callback(IPC_Channel *channel, gpointer user_data) { return cib_common_callback(channel, user_data, FALSE); } gboolean cib_null_callback(IPC_Channel *channel, gpointer user_data) { struct ha_msg *op_request = NULL; cib_client_t *cib_client = user_data; cib_client_t *hash_client = NULL; const char *type = NULL; const char *uuid_ticket = NULL; if(cib_client == NULL) { crm_err("Discarding IPC message from unknown source" " on callback channel."); return FALSE; } while(channel->ops->is_message_pending(channel)) { if (channel->ch_status == IPC_DISCONNECT) { /* The message which was pending for us is that * the IPC status is now IPC_DISCONNECT */ break; } op_request = msgfromIPC_noauth(channel); type = cl_get_string(op_request, F_CIB_OPERATION); if(safe_str_neq(type, CRM_OP_REGISTER) ) { crm_warn("Discarding IPC message from %s on callback channel", cib_client->id); ha_msg_del(op_request); continue; } uuid_ticket = cl_get_string(op_request, F_CIB_CALLBACK_TOKEN); hash_client = g_hash_table_lookup(client_list, uuid_ticket); if(hash_client != NULL) { crm_err("Duplicate registration request... disconnecting"); ha_msg_del(op_request); return FALSE; } cib_client->id = crm_strdup(uuid_ticket); g_hash_table_insert(client_list, cib_client->id, cib_client); crm_info("Registered %s on %s channel", cib_client->id, cib_client->channel_name); ha_msg_del(op_request); op_request = ha_msg_new(2); ha_msg_add(op_request, F_CIB_OPERATION, CRM_OP_REGISTER); ha_msg_add(op_request, F_CIB_CLIENTID, cib_client->id); msg2ipcchan(op_request, channel); ha_msg_del(op_request); } return cib_process_disconnect(channel, cib_client); } gboolean cib_common_callback( IPC_Channel *channel, gpointer user_data, gboolean privileged) { int rc = cib_ok; int lpc = 0; int call_type = 0; int call_options = 0; const char *op = NULL; const char *host = NULL; struct ha_msg *op_request = NULL; struct ha_msg *op_reply = NULL; cib_client_t *cib_client = user_data; if(cib_client == NULL) { crm_err("Receieved call from unknown source. Discarding."); return FALSE; } crm_verbose("Callback for %s on %s channel", cib_client->id, cib_client->channel_name); while(channel->ops->is_message_pending(channel)) { if (channel->ch_status == IPC_DISCONNECT) { /* The message which was pending for us is that * the IPC status is now IPC_DISCONNECT */ break; } op_request = msgfromIPC(channel); if (op_request == NULL) { perror("Receive failure:"); break; } crm_verbose("Processing IPC message from %s on %s channel", cib_client->id, cib_client->channel_name); cl_log_message(op_request); lpc++; if(ha_msg_add(op_request, F_CIB_CLIENTID, cib_client->id) != HA_OK) { crm_err("Couldnt add F_CIB_CLIENTID to message"); rc = cib_msg_field_add; } if(rc == cib_ok) { ha_msg_value_int( op_request, F_CIB_CALLOPTS, &call_options); crm_trace("Call options: %.8lx", (long)call_options); host = cl_get_string(op_request, F_CIB_HOST); crm_trace("Destination host: %s", host); op = cl_get_string(op_request, F_CIB_OPERATION); crm_trace("Retrieved command: %s", op); rc = cib_get_operation_id(op_request, &call_type); crm_trace("Command offset: %d", call_type); } if(rc == cib_ok && cib_server_ops[call_type].needs_privileges && privileged == FALSE) { rc = cib_not_authorized; } if(rc != cib_ok) { /* TODO: construct error reply */ crm_err("Pre-processing of command failed: %s", cib_error2string(rc)); } else if(host == NULL && cib_is_master && !(call_options & cib_scope_local)) { crm_info("Processing master %s op locally", op); rc = cib_process_command( op_request, &op_reply, privileged); } else if((host == NULL && (call_options & cib_scope_local)) || safe_str_eq(host, cib_our_uname)) { crm_info("Processing %s op locally", op); rc = cib_process_command( op_request, &op_reply, privileged); } else if(host != NULL) { crm_info("Forwarding %s op to %s", op, host); ha_msg_add(op_request, F_CIB_DELEGATED, cib_our_uname); hb_conn->llc_ops->send_ordered_nodemsg( hb_conn, op_request, host); - ha_msg_del(op_request); + + if(call_options & cib_discard_reply) { + ha_msg_del(op_request); + + } else if(call_options & cib_sync_call) { + /* keep track of the request so we can time it + * out if required + */ + crm_debug("Registering call from %s as delegated", cib_client->id); + cib_client->delegated_calls = g_list_append( + cib_client->delegated_calls, + op_request); + } else { + ha_msg_del(op_request); + } continue; } else { /* send via HA to other nodes */ crm_info("Forwarding %s op to master instance", op); ha_msg_add(op_request, F_CIB_DELEGATED, cib_our_uname); hb_conn->llc_ops->sendclustermsg(hb_conn, op_request); - ha_msg_del(op_request); + if(call_options & cib_discard_reply) { + ha_msg_del(op_request); + + } else if(call_options & cib_sync_call) { + /* keep track of the request so we can time it + * out if required + */ + crm_debug("Registering call from %s as delegated", cib_client->id); + cib_client->delegated_calls = g_list_append( + cib_client->delegated_calls, + op_request); + } else { + ha_msg_del(op_request); + } continue; } if(op_reply == NULL) { crm_trace("No reply is required for op %s", op); } else if(call_options & cib_sync_call) { crm_info("Sending sync reply %p to %s op", op_reply,op); if(msg2ipcchan(op_reply, channel) != HA_OK) { rc = cib_reply_failed; } } else { /* send reply via client's callback channel */ crm_info("Sending async reply %p to %s op", op_reply, op); rc = send_via_callback_channel( op_reply, cib_client->callback_id); } if(rc == cib_ok && cib_server_ops[call_type].modifies_cib && !(call_options & cib_scope_local)) { /* send via HA to other nodes */ crm_info("Forwarding %s op to all instances", op); ha_msg_add(op_request, F_CIB_GLOBAL_UPDATE, "true"); hb_conn->llc_ops->sendclustermsg(hb_conn, op_request); } else { if(call_options & cib_scope_local ) { crm_debug("Request not broadcast : local scope"); } if(cib_server_ops[call_type].modifies_cib == FALSE) { crm_debug("Request not broadcast : R/O call"); } if(rc != cib_ok) { crm_debug("Request not broadcast : call failed : %s", cib_error2string(rc)); } } ha_msg_del(op_request); ha_msg_del(op_reply); } crm_verbose("Processed %d messages", lpc); return cib_process_disconnect(channel, cib_client); } enum cib_errors cib_process_command(const struct ha_msg *request, struct ha_msg **reply, gboolean privileged) { xmlNodePtr input = NULL; const char *input_s = NULL; char *output_s = NULL; xmlNodePtr output = NULL; int call_type = 0; int call_options = 0; enum cib_errors rc = cib_ok; const char *op = NULL; const char *call_id = NULL; const char *section = NULL; /* Start processing the request... */ op = cl_get_string(request, F_CIB_OPERATION); call_id = cl_get_string(request, F_CIB_CALLID); ha_msg_value_int(request, F_CIB_CALLOPTS, &call_options); crm_trace("Processing call id: %s", call_id); rc = cib_get_operation_id(request, &call_type); if(rc == cib_ok && cib_server_ops[call_type].needs_privileges && privileged == FALSE) { /* abort */ rc = cib_not_authorized; } if(rc == cib_ok && cib_server_ops[call_type].needs_section) { crm_trace("Unpacking section"); section = cl_get_string(request, F_CIB_SECTION); } if(rc == cib_ok && cib_server_ops[call_type].needs_data) { crm_trace("Unpacking data in %s", F_CIB_CALLDATA); input_s = cl_get_string(request, F_CIB_CALLDATA); if(input_s != NULL) { crm_trace("Converting to xmlNodePtr"); input = string2xml(input_s); if(input == NULL) { crm_err("Invalid XML input"); rc = CIBRES_CORRUPT; } } } if(rc == cib_ok) { rc = cib_server_ops[call_type].fn( op, call_options, section, input, &output); } if(call_options & cib_discard_reply || reply == NULL) { if(reply) *reply = NULL; return rc; } /* make the basic reply */ *reply = ha_msg_new(8); ha_msg_add(*reply, F_TYPE, "cib"); ha_msg_add(*reply, F_CIB_OPERATION, op); ha_msg_add(*reply, F_CIB_CALLID, call_id); ha_msg_add_int(*reply, F_CIB_RC, rc); { const char *tmp = cl_get_string(request, F_CIB_CLIENTID); ha_msg_add(*reply, F_CIB_CLIENTID, tmp); tmp = cl_get_string(request, F_CIB_CALLOPTS); ha_msg_add(*reply, F_CIB_CALLOPTS, tmp); tmp = cl_get_string(request, F_CIB_CALLID); ha_msg_add(*reply, F_CIB_CALLID, tmp); } /* attach the output if necessary */ output_s = dump_xml_unformatted(output); if(output != NULL && output_s == NULL) { crm_err("Currupt output in reply to \"%s\" op",op); rc = cib_output_data; } else if(output_s != NULL && ha_msg_add(*reply, F_CIB_CALLDATA, output_s) != HA_OK) { rc = cib_msg_field_add; } crm_free(output_s); free_xml(output); free_xml(input); return rc; } int send_via_callback_channel(struct ha_msg *msg, const char *token) { cib_client_t *hash_client = NULL; + GList *list_item = NULL; crm_debug("Delivering msg %p to client %s", msg, token); if(msg == NULL) { crm_err("No message to send"); return cib_reply_failed; } else if(token == NULL) { crm_err("No client id token, cant send message"); return cib_missing; } - + hash_client = g_hash_table_lookup(client_list, token); if(hash_client == NULL) { crm_err("Cannot find client for token %s", token); return cib_client_gone; } else if(hash_client->channel == NULL) { crm_err("Cannot find channel for client %s", token); return cib_client_corrupt; } + list_item = g_list_find_custom( + hash_client->delegated_calls, msg, cib_GCompareFunc); + + if(list_item != NULL) { + /* remove it - no need to time it out */ + struct ha_msg *orig_msg = list_item->data; + crm_debug("Removing msg from delegated list"); + hash_client->delegated_calls = g_list_remove( + hash_client->delegated_calls, orig_msg); + ha_msg_del(orig_msg); + } + crm_debug("Delivering reply to client %s", token); if(msg2ipcchan(msg, hash_client->channel) != HA_OK) { crm_err("Delivery of reply to client %s failed", token); return cib_reply_failed; } return cib_ok; } +gint cib_GCompareFunc(gconstpointer a, gconstpointer b) +{ + const struct ha_msg *a_msg = a; + const struct ha_msg *b_msg = b; + + int msg_a_id = 0; + int msg_b_id = 0; + + ha_msg_value_int(a_msg, F_CIB_CALLID, &msg_a_id); + ha_msg_value_int(b_msg, F_CIB_CALLID, &msg_b_id); + + if(msg_a_id == msg_b_id) { + return 0; + } else if(msg_a_id < msg_b_id) { + return -1; + } + return 1; +} + + +gboolean +cib_msg_timeout(gpointer data) +{ + crm_trace("Checking if any clients have timed out messages"); + g_hash_table_foreach(client_list, cib_GHFunc, NULL); + return TRUE; +} + + +void +cib_GHFunc(gpointer key, gpointer value, gpointer user_data) +{ + cib_client_t *client = value; + + GListPtr list = client->delegated_calls; + struct ha_msg *msg = NULL; + + + while(list != NULL) { + struct ha_msg *reply = ha_msg_new(4); + int seen = 0; + int timeout = 5; /* 1 iteration == 1 seconds */ + + msg = list->data; + ha_msg_value_int(msg, F_CIB_SEENCOUNT, &seen); + ha_msg_value_int(msg, F_CIB_TIMEOUT, &timeout); + + crm_trace("Timeout %d, seen %d", timeout, seen); + if(timeout > 0 && seen < timeout) { + int seen2 = 0; + crm_trace("Updating seen count for msg from client %s", + client->id); + seen++; + ha_msg_mod_int(msg, F_CIB_SEENCOUNT, seen); + ha_msg_value_int(msg, F_CIB_SEENCOUNT, &seen2); + list = list->next; + continue; + } + + crm_warn("Sending operation timeout msg to client %s", + client->id); + + ha_msg_add(reply, F_TYPE, "cib"); + ha_msg_add(reply, F_CIB_OPERATION, + cl_get_string(msg, F_CIB_OPERATION)); + ha_msg_add(reply, F_CIB_CALLID, + cl_get_string(msg, F_CIB_CALLID)); + ha_msg_add_int(reply, F_CIB_RC, cib_master_timeout); + + msg2ipcchan(reply, client->channel); + + list = list->next; + client->delegated_calls = g_list_remove( + client->delegated_calls, msg); + + ha_msg_del(reply); + ha_msg_del(msg); + } +} + gboolean cib_process_disconnect(IPC_Channel *channel, cib_client_t *cib_client) { if (channel->ch_status == IPC_DISCONNECT && cib_client != NULL) { - crm_info("Cleaning up after %s channel disconnect from client %s", - cib_client->channel_name, cib_client->id); + crm_info("Cleaning up after %s channel disconnect from client (%p) %s", + cib_client->channel_name, cib_client, cib_client->id); g_hash_table_remove(client_list, cib_client->id); if(cib_client->source != NULL) { - G_main_del_IPC_Channel(cib_client->source); + crm_debug("deleting the IPC Channel"); + G_main_del_IPC_Channel(cib_client->source); cib_client->source = NULL; } + crm_debug("Freeing the cib client"); + crm_debug("Freeing the cib client %s", cib_client->id); /* crm_free(cib_client->callback_id); */ -/* crm_free(cib_client->id); */ - crm_free(cib_client); + crm_free(cib_client->id); + crm_free(cib_client); + crm_debug("Freed the cib client"); return FALSE; } else if (channel->ch_status == IPC_DISCONNECT) { crm_warn("Unknown client disconnected"); return FALSE; } return TRUE; } gboolean cib_ha_dispatch(IPC_Channel *channel, gpointer user_data) { int lpc = 0; ll_cluster_t *hb_cluster = (ll_cluster_t*)user_data; while(hb_cluster->llc_ops->msgready(hb_cluster)) { lpc++; /* invoke the callbacks but dont block */ hb_cluster->llc_ops->rcvmsg(hb_cluster, 0); } crm_trace("%d HA messages dispatched", lpc); if (channel && (channel->ch_status == IPC_DISCONNECT)) { crm_crit("Lost connection to heartbeat service... exiting"); exit(100); return FALSE; } return TRUE; } void cib_peer_callback(const struct ha_msg* msg, void* private_data) { int is_done = 1; int call_type = 0; int call_options = 0; gboolean process = TRUE; gboolean needs_reply = TRUE; gboolean local_notify = FALSE; enum cib_errors rc = cib_ok; struct ha_msg *op_reply = NULL; const char *originator = cl_get_string(msg, F_ORIG); const char *request_to = cl_get_string(msg, F_CIB_HOST); const char *reply_to = cl_get_string(msg, F_CIB_ISREPLY); const char *update = cl_get_string(msg, F_CIB_GLOBAL_UPDATE); const char *delegated = cl_get_string(msg, F_CIB_DELEGATED); const char *client_id = NULL; if(safe_str_eq(originator, cib_our_uname)) { - crm_debug("Discarding message from ourselves"); + crm_debug("Discarding message %s from ourselves", + cl_get_string(msg, F_SEQ)); return; } if(cib_get_operation_id(msg, &call_type) != cib_ok) { - crm_err("Invalid operation... discarding msg"); + crm_err("Invalid operation... discarding msg %s", + cl_get_string(msg, F_SEQ)); return; } + crm_trace("%s Processing msg %s", + cib_our_uname, cl_get_string(msg, F_SEQ)); + if(request_to != NULL && strlen(request_to) == 0) { request_to = NULL; } if(cib_server_ops[call_type].modifies_cib || (reply_to == NULL && cib_is_master) || request_to != NULL) { is_done = 0; } crm_info("Processing message from peer to %s...", request_to); cl_log_message(msg); if(safe_str_eq(update, "true") && safe_str_eq(reply_to, cib_our_uname)) { crm_debug("Processing global update that originated from us"); needs_reply = FALSE; local_notify = TRUE; } else if(safe_str_eq(update, "true")) { crm_debug("Processing global update"); needs_reply = FALSE; } else if(request_to != NULL && safe_str_eq(request_to, cib_our_uname)) { crm_debug("Processing request sent to us"); } else if(delegated != NULL && cib_is_master == TRUE) { crm_debug("Processing request sent to master instance"); } else if(reply_to != NULL && safe_str_eq(reply_to, cib_our_uname)) { crm_debug("Forward reply sent from %s to local clients", originator); process = FALSE; needs_reply = FALSE; local_notify = TRUE; } else if(delegated != NULL) { crm_debug("Ignoring msg for master instance"); return; } else if(request_to != NULL) { /* this is for a specific instance and we're not it */ crm_debug("Ignoring msg for instance on %s", request_to); return; } else if(reply_to == NULL && cib_is_master == FALSE) { /* this is for the master instance and we're not it */ crm_debug("Ignoring reply to %s", reply_to); return; } else { crm_warn("Nothing for us to do?"); return; } ha_msg_value_int(msg, F_CIB_CALLOPTS, &call_options); crm_trace("Retrieved call options: %d", call_options); if(process) { crm_debug("Performing local processing"); rc = cib_process_command(msg, &op_reply, TRUE); } if(local_notify) { /* send callback to originating child */ cib_client_t *client_obj = NULL; crm_trace("find the client"); if(process == FALSE) { op_reply = ha_msg_copy(msg); } - client_id = cl_get_string(msg, F_CIB_CLIENTID); if(client_id != NULL) { client_obj = g_hash_table_lookup( client_list, client_id); } else { crm_err("No client to sent the response to." " F_CIB_CLIENTID not set."); } crm_debug("Sending callback to originator of delegated request"); if(client_obj != NULL) { if(is_done == 0) { crm_debug("Sending local modify response"); } else { crm_debug("Sending master response"); } if(call_options & cib_sync_call) { - crm_debug("Sending sync response: %d", call_options); - msg2ipcchan(op_reply, client_obj->channel); + crm_debug("Sending sync response: %d", + call_options); + + send_via_callback_channel( + op_reply, client_obj->id); +/* msg2ipcchan(op_reply, client_obj->channel); */ } else { crm_debug("Sending async response"); send_via_callback_channel( op_reply, client_obj->callback_id); } } else { crm_warn("Client %s may have left us", client_id); } if(process == FALSE) { ha_msg_del(op_reply); } } if(needs_reply == FALSE) { /* nothing more to do... * this was a non-originating slave update */ crm_debug("Completed slave update"); return; } crm_trace("add the originator to message"); ha_msg_add(op_reply, F_CIB_ISREPLY, originator); /* from now on we are the server */ if(rc == cib_ok && cib_server_ops[call_type].modifies_cib && !(call_options & cib_scope_local)) { /* this (successful) call modified the CIB _and_ the * change needs to be broadcast... * send via HA to other nodes */ crm_debug("Sending update request to everyone"); hb_conn->llc_ops->sendclustermsg(hb_conn, op_reply); } else { /* send reply via HA to originating node */ crm_debug("Sending request result to originator only"); hb_conn->llc_ops->send_ordered_nodemsg( hb_conn, op_reply, originator); } return; } enum cib_errors cib_get_operation_id(const struct ha_msg* msg, int *operation) { int lpc = 0; int max_msg_types = DIMOF(cib_server_ops); const char *op = cl_get_string(msg, F_CIB_OPERATION); for (lpc = 0; lpc < max_msg_types; lpc++) { if (safe_str_eq(op, cib_server_ops[lpc].operation)) { *operation = lpc; return cib_ok; } } crm_err("Operation %s is not valid", op); *operation = -1; return cib_operation; } diff --git a/crm/cib/callbacks.h b/crm/cib/callbacks.h index d29d69bbec..345a9650d7 100644 --- a/crm/cib/callbacks.h +++ b/crm/cib/callbacks.h @@ -1,66 +1,67 @@ -/* $Id: callbacks.h,v 1.1 2004/12/05 16:14:07 andrew Exp $ */ +/* $Id: callbacks.h,v 1.2 2004/12/16 14:34:18 andrew Exp $ */ /* * 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 extern gboolean cib_is_master; extern GHashTable *client_list; typedef struct cib_client_s { char *id; char *callback_id; const char *channel_name; IPC_Channel *channel; GCHSource *source; + GList *delegated_calls; } cib_client_t; typedef struct cib_operation_s { const char* operation; gboolean modifies_cib; gboolean needs_privileges; gboolean needs_section; gboolean needs_data; enum cib_errors (*fn)( const char *, int, const char *, xmlNodePtr, xmlNodePtr*); } cib_operation_t; extern cib_operation_t cib_server_ops[]; extern gboolean cib_client_connect(IPC_Channel *channel, gpointer user_data); extern gboolean cib_null_callback (IPC_Channel *channel, gpointer user_data); extern gboolean cib_rw_callback (IPC_Channel *channel, gpointer user_data); extern gboolean cib_ro_callback (IPC_Channel *channel, gpointer user_data); extern gboolean cib_ha_dispatch (IPC_Channel *channel, gpointer user_data); extern void cib_peer_callback(const struct ha_msg* msg, void* private_data); diff --git a/crm/cib/main.c b/crm/cib/main.c index d7d33abd3b..1b158cf897 100644 --- a/crm/cib/main.c +++ b/crm/cib/main.c @@ -1,318 +1,310 @@ -/* $Id: main.c,v 1.4 2004/12/15 16:19:42 andrew Exp $ */ +/* $Id: main.c,v 1.5 2004/12/16 14:34:18 andrew Exp $ */ /* * 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 #include #include #include /* #define REALTIME_SUPPORT 0 */ #define PID_FILE WORKING_DIR"/"CRM_SYSTEM_CIB".pid" #define DAEMON_LOG DEVEL_DIR"/"CRM_SYSTEM_CIB".log" #define DAEMON_DEBUG DEVEL_DIR"/"CRM_SYSTEM_CIB".debug" GMainLoop* mainloop = NULL; const char* crm_system_name = CRM_SYSTEM_CIB; const char *cib_our_uname = NULL; void usage(const char* cmd, int exit_status); int init_start(void); gboolean cib_register_ha(ll_cluster_t *hb_cluster, const char *client_name); void cib_shutdown(int nsig); void cib_ha_connection_destroy(gpointer user_data); gboolean startCib(const char *filename); +extern gboolean cib_msg_timeout(gpointer data); ll_cluster_t *hb_conn = NULL; #define OPTARGS "skrhV" int main(int argc, char ** argv) { int req_comms_restart = FALSE; int req_restart = FALSE; int req_status = FALSE; int req_stop = FALSE; int argerr = 0; int flag; #ifdef DEVEL_DIR mkdir(DEVEL_DIR, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); #endif /* Redirect messages from glib functions to our handler */ 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... */ g_log_set_always_fatal((GLogLevelFlags)0); cl_log_set_entity (crm_system_name); cl_log_set_facility (LOG_LOCAL7); cl_log_set_logfile (DAEMON_LOG); cl_log_set_debugfile(DAEMON_DEBUG); CL_SIGNAL(DEBUG_INC, alter_debug); CL_SIGNAL(DEBUG_DEC, alter_debug); CL_SIGNAL(SIGTERM, cib_shutdown); client_list = g_hash_table_new(&g_str_hash, &g_str_equal); while ((flag = getopt(argc, argv, OPTARGS)) != EOF) { switch(flag) { case 'V': alter_debug(DEBUG_INC); break; case 's': /* Status */ req_status = TRUE; break; case 'r': /* Restart */ req_restart = TRUE; break; case 'c': /* Restart */ req_comms_restart = TRUE; break; case 'h': /* Help message */ usage(crm_system_name, LSB_EXIT_OK); break; default: ++argerr; break; } } set_crm_log_level(LOG_TRACE); cl_set_corerootdir(DEVEL_DIR); cl_enable_coredumps(1); cl_cdtocoredir(); if (optind > argc) { ++argerr; } if (argerr) { usage(crm_system_name,LSB_EXIT_GENERIC); } /* read local config file */ if (req_status){ return init_status(PID_FILE, crm_system_name); } if (req_stop){ return init_stop(PID_FILE); } if (req_restart) { init_stop(PID_FILE); } return init_start(); } int init_start(void) { gboolean was_error = FALSE; -#ifdef REALTIME_SUPPORT - static int crm_realtime = 1; -#endif hb_conn = ll_cluster_new("heartbeat"); cib_register_ha(hb_conn, CRM_SYSTEM_CIB); if(startCib(CIB_FILENAME) == FALSE){ crm_crit("Cannot start CIB... terminating"); exit(1); } was_error = init_server_ipc_comms( crm_strdup("cib_callback"), cib_client_connect, default_ipc_connection_destroy); was_error = was_error || init_server_ipc_comms( crm_strdup("cib_ro"), cib_client_connect, default_ipc_connection_destroy); was_error = was_error || init_server_ipc_comms( crm_strdup("cib_rw"), cib_client_connect, default_ipc_connection_destroy); if(was_error == FALSE) { /* Create the mainloop and run it... */ mainloop = g_main_new(FALSE); crm_info("Starting %s mainloop", crm_system_name); -#ifdef REALTIME_SUPPORT - if (crm_realtime == 1) { - cl_enable_realtime(); - - } else if (crm_realtime == 0) { - cl_disable_realtime(); - } - cl_make_realtime(SCHED_RR, 5, 64, 64); -#endif + Gmain_timeout_add(1000, cib_msg_timeout, NULL); + g_main_run(mainloop); return_to_orig_privs(); } else { crm_err("Couldnt start all communication channels, exiting."); } return 0; } void usage(const char* cmd, int exit_status) { FILE* stream; stream = exit_status ? stderr : stdout; fprintf(stream, "usage: %s [-srkh]" "[-c configure file]\n", cmd); /* fprintf(stream, "\t-d\tsets debug level\n"); */ /* fprintf(stream, "\t-s\tgets daemon status\n"); */ /* fprintf(stream, "\t-r\trestarts daemon\n"); */ /* fprintf(stream, "\t-k\tstops daemon\n"); */ /* fprintf(stream, "\t-h\thelp message\n"); */ fflush(stream); exit(exit_status); } gboolean cib_register_ha(ll_cluster_t *hb_cluster, const char *client_name) { int facility; if(safe_val3(NULL, hb_cluster, llc_ops, errmsg) == NULL) { crm_crit("cluster errmsg function unavailable"); } crm_info("Signing in with Heartbeat"); if (hb_cluster->llc_ops->signon(hb_cluster, client_name)!= HA_OK) { crm_err("Cannot sign on with heartbeat: %s", hb_cluster->llc_ops->errmsg(hb_cluster)); return FALSE; } /* change the logging facility to the one used by heartbeat daemon */ crm_info("Switching to Heartbeat logger"); if (( facility = hb_cluster->llc_ops->get_logfacility(hb_cluster)) > 0) { cl_log_set_facility(facility); } crm_verbose("Facility: %d", facility); crm_debug("Be informed of CIB messages"); if (HA_OK != hb_cluster->llc_ops->set_msg_callback( hb_cluster, T_CIB, cib_peer_callback, hb_cluster)){ crm_err("Cannot set msg callback: %s", hb_cluster->llc_ops->errmsg(hb_cluster)); return FALSE; } crm_debug("Finding our node name"); if ((cib_our_uname = hb_cluster->llc_ops->get_mynodeid(hb_cluster)) == NULL) { crm_err("get_mynodeid() failed"); return FALSE; } crm_info("FSA Hostname: %s", cib_our_uname); crm_debug("Adding channel to mainloop"); G_main_add_IPC_Channel( G_PRIORITY_HIGH, hb_cluster->llc_ops->ipcchan(hb_cluster), FALSE, cib_ha_dispatch, hb_cluster /* userdata */, cib_ha_connection_destroy); return TRUE; } void cib_ha_connection_destroy(gpointer user_data) { } void cib_shutdown(int nsig) { static int shuttingdown = 0; CL_SIGNAL(nsig, cib_shutdown); if (!shuttingdown) { shuttingdown = 1; } if (mainloop != NULL && g_main_is_running(mainloop)) { g_main_quit(mainloop); } else { exit(LSB_EXIT_OK); } } gboolean startCib(const char *filename) { xmlNodePtr cib = readCibXmlFile(filename); if (initializeCib(cib)) { crm_info("CIB Initialization completed successfully"); } else { /* free_xml(cib); */ crm_warn("CIB Initialization failed, " "starting with an empty default."); activateCibXml(createEmptyCib(), filename); } return TRUE; } diff --git a/crm/crmd/callbacks.c b/crm/crmd/callbacks.c index e7f7bda373..341a3efe5e 100644 --- a/crm/crmd/callbacks.c +++ b/crm/crmd/callbacks.c @@ -1,518 +1,518 @@ /* * 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 FILE *msg_in_strm = NULL; FILE *msg_ipc_strm = NULL; xmlNodePtr find_xml_in_hamessage(const struct ha_msg* msg); void crmd_ha_connection_destroy(gpointer user_data); void crmd_ha_msg_callback(const struct ha_msg* msg, void* private_data) { const char *to = NULL; char *xml_text = NULL; xmlNodePtr root_xml_node = NULL; const char *from = ha_msg_value(msg, F_ORIG); const char *seq = ha_msg_value(msg, F_SEQ); const char *type = ha_msg_value(msg, F_TYPE); #ifdef MSG_LOG if(msg_in_strm == NULL) { msg_in_strm = fopen(DEVEL_DIR"/inbound.log", "w"); } #endif if(from == NULL) { crm_err("Value of %s was NULL", F_ORIG); } else if(safe_str_eq(from, fsa_our_uname)) { if(safe_str_eq(type, T_CRM)) { #ifdef MSG_LOG fprintf(msg_in_strm, "Discarded %s message [F_SEQ=%s] from ourselves.\n", T_CRM, seq); fflush(msg_in_strm); #endif return; } } root_xml_node = find_xml_in_hamessage(msg); to = xmlGetProp(root_xml_node, XML_ATTR_HOSTTO); #ifdef MSG_LOG xml_text = dump_xml_formatted(root_xml_node); fprintf(msg_in_strm, "[%s (%s:%s)]\t%s\n", crm_str(from), seq, ha_msg_value(msg, F_TYPE), xml_text ); fflush(msg_in_strm); crm_free(xml_text); #endif if(to != NULL && strlen(to) > 0 && strcmp(to, fsa_our_uname) != 0) { #ifdef MSG_LOG fprintf(msg_in_strm, "Discarding message [F_SEQ=%s] for someone else", seq); #endif return; } set_xml_property_copy(root_xml_node, XML_ATTR_HOSTFROM, from); set_xml_property_copy(root_xml_node, "F_SEQ", seq); register_fsa_input(C_HA_MESSAGE, I_ROUTER, root_xml_node); s_crmd_fsa(C_HA_MESSAGE); free_xml(root_xml_node); return; } /* * Apparently returning TRUE means "stay connected, keep doing stuff". * Returning FALSE means "we're all done, close the connection" */ gboolean crmd_ipc_msg_callback(IPC_Channel *client, gpointer user_data) { int lpc = 0; char *buffer = NULL; IPC_Message *msg = NULL; gboolean hack_return_good = TRUE; xmlNodePtr root_xml_node; crmd_client_t *curr_client = (crmd_client_t*)user_data; crm_verbose("Processing IPC message from %s", curr_client->table_key); #ifdef MSG_LOG if(msg_ipc_strm == NULL) { msg_ipc_strm = fopen(DEVEL_DIR"/inbound.ipc.log", "w"); } #endif while(client->ops->is_message_pending(client)) { if (client->ch_status == IPC_DISCONNECT) { /* The message which was pending for us is that * the IPC status is now IPC_DISCONNECT */ break; } if (client->ops->recv(client, &msg) != IPC_OK) { perror("Receive failure:"); #ifdef MSG_LOG fprintf(msg_ipc_strm, "[%s] [receive failure]\n", curr_client->table_key); fflush(msg_in_strm); #endif return !hack_return_good; } if (msg == NULL) { #ifdef MSG_LOG fprintf(msg_ipc_strm, "[%s] [__nothing__]\n", curr_client->table_key); fflush(msg_in_strm); #endif crm_err("No message this time"); continue; } lpc++; buffer = (char*)msg->msg_body; crm_verbose("Processing xml from %s [text=%s]\n", curr_client->table_key, buffer); #ifdef MSG_LOG fprintf(msg_ipc_strm, "[%s] [text=%s]\n", curr_client->table_key, buffer); fflush(msg_in_strm); #endif root_xml_node = find_xml_in_ipcmessage(msg, FALSE); if (root_xml_node != NULL) { crmd_authorize_message( root_xml_node, msg, curr_client); } else { crm_info("IPC Message was not valid... discarding."); } free_xml(root_xml_node); msg->msg_done(msg); msg = NULL; buffer = NULL; root_xml_node = NULL; } crm_verbose("Processed %d messages", lpc); if (client->ch_status == IPC_DISCONNECT) { crm_info("received HUP from %s", curr_client->table_key); if (curr_client != NULL) { struct crm_subsystem_s *the_subsystem = NULL; if (curr_client->sub_sys == NULL) { crm_warn("Client hadn't registered with us yet"); } else if (strcmp(CRM_SYSTEM_PENGINE, curr_client->sub_sys) == 0) { the_subsystem = pe_subsystem; } else if (strcmp(CRM_SYSTEM_TENGINE, curr_client->sub_sys) == 0) { the_subsystem = te_subsystem; } else if (strcmp(CRM_SYSTEM_CIB, curr_client->sub_sys) == 0){ the_subsystem = cib_subsystem; } if(the_subsystem != NULL) { cleanup_subsystem(the_subsystem); } /* else that was a transient client */ if (curr_client->table_key != NULL) { /* * Key is destroyed below: * curr_client->table_key * Value is cleaned up by: * G_main_del_IPC_Channel */ g_hash_table_remove( ipc_clients, curr_client->table_key); } if(curr_client->client_source != NULL) { gboolean det = G_main_del_IPC_Channel( curr_client->client_source); crm_verbose("crm_client was %s detached", det?"successfully":"not"); } crm_free(curr_client->table_key); crm_free(curr_client->sub_sys); crm_free(curr_client->uuid); crm_free(curr_client); } return !hack_return_good; } return hack_return_good; } void -lrm_op_callback (lrm_op_t* op) +lrm_op_callback(lrm_op_t* op) { /* todo: free op->rsc */ crm_debug("received callback"); register_fsa_input(C_LRM_OP_CALLBACK, I_LRM_EVENT, op); s_crmd_fsa(C_LRM_OP_CALLBACK); } void crmd_ha_status_callback( const char *node, const char * status, void* private_data) { xmlNodePtr update = NULL; xmlNodePtr fragment = NULL; crm_debug("received callback"); crm_notice("Status update: Node %s now has status [%s]\n",node,status); if(AM_I_DC == FALSE) { crm_debug("Got nstatus callback in non-DC mode"); return; } else if(safe_str_neq(status, DEADSTATUS)) { crm_debug("nstatus callback was not for a dead node"); return; } /* this node is taost */ update = create_node_state( node, node, status, NULL, NULL, NULL, NULL); set_xml_property_copy( update, XML_CIB_ATTR_CLEAR_SHUTDOWN, XML_BOOLEAN_TRUE); fragment = create_cib_fragment(update, NULL); crm_xml_debug(fragment, "Node status update"); update_local_cib(fragment, TRUE); free_xml(fragment); free_xml(update); } void crmd_client_status_callback(const char * node, const char * client, const char * status, void * private) { const char *join = NULL; const char *extra = NULL; xmlNodePtr update = NULL; xmlNodePtr fragment = NULL; crm_debug("received callback"); set_bit_inplace(fsa_input_register, R_PEER_DATA); if(safe_str_eq(status, JOINSTATUS)){ status = ONLINESTATUS; extra = XML_CIB_ATTR_CLEAR_SHUTDOWN; } else if(safe_str_eq(status, LEAVESTATUS)){ status = OFFLINESTATUS; join = CRMD_JOINSTATE_DOWN; extra = XML_CIB_ATTR_CLEAR_SHUTDOWN; } crm_notice("Status update: Client %s/%s now has status [%s]\n", node, client, status); if(AM_I_DC == FALSE) { crm_debug("Got client status callback in non-DC mode"); return; } update = create_node_state( node, node, NULL, NULL, status, join, NULL); set_xml_property_copy(update, extra, XML_BOOLEAN_TRUE); fragment = create_cib_fragment(update, NULL); crm_xml_debug(fragment, "Client status update"); update_local_cib(fragment, TRUE); free_xml(fragment); free_xml(update); } xmlNodePtr find_xml_in_hamessage(const struct ha_msg* msg) { const char *xml; xmlDocPtr doc; xmlNodePtr root; if (msg == NULL) { crm_info("**** ha_crm_msg_callback called on a NULL message"); return NULL; } #if 0 crm_debug("[F_TYPE=%s]", ha_msg_value(msg, F_TYPE)); crm_debug("[F_ORIG=%s]", ha_msg_value(msg, F_ORIG)); crm_debug("[F_TO=%s]", ha_msg_value(msg, F_TO)); crm_debug("[F_COMMENT=%s]", ha_msg_value(msg, F_COMMENT)); crm_debug("[F_XML=%s]", ha_msg_value(msg, "xml")); /* crm_debug("[F_=%s]", ha_msg_value(ha_msg, F_)); */ #endif if (strcmp(T_CRM, ha_msg_value(msg, F_TYPE)) != 0) { crm_info("Received a (%s) message by mistake.", ha_msg_value(msg, F_TYPE)); return NULL; } xml = ha_msg_value(msg, "xml"); if (xml == NULL) { crm_info("No XML attached to this message."); return NULL; } doc = xmlParseMemory(xml, strlen(xml)); if (doc == NULL) { crm_info("XML Buffer was not valid."); return NULL; } root = xmlDocGetRootElement(doc); if (root == NULL) { crm_info("Root node was NULL."); return NULL; } return root; } gboolean lrm_dispatch(int fd, gpointer user_data) { int rc = 0; ll_lrm_t *lrm = (ll_lrm_t*)user_data; crm_debug("received callback"); rc = lrm->lrm_ops->rcvmsg(lrm, FALSE); return TRUE; } /* #define MAX_EMPTY_CALLBACKS 20 */ /* int empty_callbacks = 0; */ gboolean crmd_ha_msg_dispatch(IPC_Channel *channel, gpointer user_data) { int lpc = 0; ll_cluster_t *hb_cluster = (ll_cluster_t*)user_data; while(hb_cluster->llc_ops->msgready(hb_cluster)) { lpc++; /* invoke the callbacks but dont block */ hb_cluster->llc_ops->rcvmsg(hb_cluster, 0); } crm_trace("%d HA messages dispatched", lpc); if (channel && (channel->ch_status == IPC_DISCONNECT)) { crm_crit("Lost connection to heartbeat service."); return FALSE; } return TRUE; } void crmd_ha_connection_destroy(gpointer user_data) { crm_crit("Heartbeat has left us"); /* this is always an error */ /* feed this back into the FSA */ register_fsa_input(C_HA_DISCONNECT, I_ERROR, NULL); s_crmd_fsa(C_HA_DISCONNECT); } gboolean crmd_client_connect(IPC_Channel *client_channel, gpointer user_data) { if (client_channel == NULL) { crm_err("Channel was NULL"); } else if (client_channel->ch_status == IPC_DISCONNECT) { crm_err("Channel was disconnected"); } else { crmd_client_t *blank_client = NULL; crm_debug("Channel connected"); crm_malloc(blank_client, sizeof(crmd_client_t)); if (blank_client == NULL) { return FALSE; } client_channel->ops->set_recv_qlen(client_channel, 100); client_channel->ops->set_send_qlen(client_channel, 100); blank_client->client_channel = client_channel; blank_client->sub_sys = NULL; blank_client->uuid = NULL; blank_client->table_key = NULL; blank_client->client_source = G_main_add_IPC_Channel( G_PRIORITY_LOW, client_channel, FALSE, crmd_ipc_msg_callback, blank_client, default_ipc_connection_destroy); } return TRUE; } gboolean ccm_dispatch(int fd, gpointer user_data) { int rc = 0; oc_ev_t *ccm_token = (oc_ev_t*)user_data; crm_debug("received callback"); rc = oc_ev_handle_event(ccm_token); if(0 == rc) { return TRUE; } else { crm_err("CCM connection appears to have failed: rc=%d.", rc); register_fsa_input(C_CCM_CALLBACK, I_ERROR, NULL); s_crmd_fsa(C_CCM_CALLBACK); return FALSE; } } void crmd_ccm_msg_callback( oc_ed_t event, void *cookie, size_t size, const void *data) { struct crmd_ccm_data_s *event_data = NULL; crm_debug("received callback"); if(data != NULL) { crm_malloc(event_data, sizeof(struct crmd_ccm_data_s)); if(event_data != NULL) { event_data->event = &event; event_data->oc = copy_ccm_oc_data( (const oc_ev_membership_t *)data); crm_debug("Sending callback to the FSA"); register_fsa_input( C_CCM_CALLBACK, I_CCM_EVENT, (void*)event_data); s_crmd_fsa(C_CCM_CALLBACK); event_data->event = NULL; event_data->oc = NULL; crm_free(event_data); } } else { crm_info("CCM Callback with NULL data... " "I dont /think/ this is bad"); } oc_ev_callback_done(cookie); return; } diff --git a/crm/crmd/lrm.c b/crm/crmd/lrm.c index 5d74adc491..06aa1ce32e 100644 --- a/crm/crmd/lrm.c +++ b/crm/crmd/lrm.c @@ -1,828 +1,836 @@ /* * 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 /* for access */ #include #include #include #include #include #include #include #include #include #include gboolean stop_all_resources(void); gboolean build_suppported_RAs( xmlNodePtr metadata_list, xmlNodePtr xml_agent_list); gboolean build_active_RAs(xmlNodePtr rsc_list); void do_update_resource(lrm_rsc_t *rsc, lrm_op_t *op); enum crmd_fsa_input do_lrm_rsc_op( lrm_rsc_t *rsc, char *rid, const char *operation, xmlNodePtr msg); enum crmd_fsa_input do_fake_lrm_op(gpointer data); GHashTable *xml2list(xmlNodePtr parent, const char **attr_path, int depth); GHashTable *monitors = NULL; int num_lrm_register_fails = 0; int max_lrm_register_fails = 30; const char *rsc_path[] = { "msg_data", "rsc_op", "resource", "instance_attributes", "rsc_parameters" }; enum crmd_rscstate { crmd_rscstate_NULL, crmd_rscstate_START, crmd_rscstate_START_PENDING, crmd_rscstate_START_OK, crmd_rscstate_START_FAIL, crmd_rscstate_STOP, crmd_rscstate_STOP_PENDING, crmd_rscstate_STOP_OK, crmd_rscstate_STOP_FAIL, crmd_rscstate_MON, crmd_rscstate_MON_PENDING, crmd_rscstate_MON_OK, crmd_rscstate_MON_FAIL, crmd_rscstate_GENERIC_PENDING, crmd_rscstate_GENERIC_OK, crmd_rscstate_GENERIC_FAIL }; void free_lrm_op(lrm_op_t *op); const char *crmd_rscstate2string(enum crmd_rscstate state); const char * crmd_rscstate2string(enum crmd_rscstate state) { switch(state) { case crmd_rscstate_NULL: return NULL; case crmd_rscstate_START: return CRMD_RSCSTATE_START; case crmd_rscstate_START_PENDING: return CRMD_RSCSTATE_START_PENDING; case crmd_rscstate_START_OK: return CRMD_RSCSTATE_START_OK; case crmd_rscstate_START_FAIL: return CRMD_RSCSTATE_START_FAIL; case crmd_rscstate_STOP: return CRMD_RSCSTATE_STOP; case crmd_rscstate_STOP_PENDING: return CRMD_RSCSTATE_STOP_PENDING; case crmd_rscstate_STOP_OK: return CRMD_RSCSTATE_STOP_OK; case crmd_rscstate_STOP_FAIL: return CRMD_RSCSTATE_STOP_FAIL; case crmd_rscstate_MON: return CRMD_RSCSTATE_MON; case crmd_rscstate_MON_PENDING: return CRMD_RSCSTATE_MON_PENDING; case crmd_rscstate_MON_OK: return CRMD_RSCSTATE_MON_OK; case crmd_rscstate_MON_FAIL: return CRMD_RSCSTATE_MON_FAIL; case crmd_rscstate_GENERIC_PENDING: return CRMD_RSCSTATE_GENERIC_PENDING; case crmd_rscstate_GENERIC_OK: return CRMD_RSCSTATE_GENERIC_OK; case crmd_rscstate_GENERIC_FAIL: return CRMD_RSCSTATE_GENERIC_FAIL; } return ""; } /* A_LRM_CONNECT */ enum crmd_fsa_input 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) { enum crmd_fsa_input failed = I_FAIL; int ret = HA_OK; if(action & A_LRM_DISCONNECT) { fsa_lrm_conn->lrm_ops->signoff(fsa_lrm_conn); /* TODO: Clean up the hashtable */ } if(action & A_LRM_CONNECT) { crm_trace("LRM: connect..."); monitors = g_hash_table_new(g_str_hash, g_str_equal); fsa_lrm_conn = ll_lrm_new(XML_CIB_TAG_LRM); if(NULL == fsa_lrm_conn) { return failed; } crm_trace("LRM: sigon..."); 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); startTimer(wait_timer); crmd_fsa_stall(); return I_NULL; } else { crm_err("Failed to sign on to the LRM %d" " (max) times", num_lrm_register_fails); return failed; } } crm_trace("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"); return failed; } /* TODO: create a destroy handler that causes * some recovery to happen */ G_main_add_fd(G_PRIORITY_LOW, fsa_lrm_conn->lrm_ops->inputfd(fsa_lrm_conn), FALSE, lrm_dispatch, fsa_lrm_conn, default_ipc_connection_destroy); set_bit_inplace(fsa_input_register, R_LRM_CONNECTED); } if(action & ~(A_LRM_CONNECT|A_LRM_DISCONNECT)) { crm_err("Unexpected action %s in %s", fsa_action2string(action), __FUNCTION__); } return I_NULL; } gboolean build_suppported_RAs(xmlNodePtr metadata_list, xmlNodePtr xml_agent_list) { GList *types = NULL; GList *classes = NULL; const char *version = NULL; const char *ra_data = NULL; /* GHashTable *metadata = NULL; */ xmlNodePtr xml_agent = NULL; xmlNodePtr xml_metadata = NULL; xmlNodePtr tmp = NULL; classes = fsa_lrm_conn->lrm_ops->get_rsc_class_supported(fsa_lrm_conn); slist_iter( class, char, classes, lpc, types = fsa_lrm_conn->lrm_ops->get_rsc_type_supported( fsa_lrm_conn, class); slist_iter( type, char, types, llpc, version = "1"; xml_agent = create_xml_node( xml_agent_list, "lrm_agent"); set_xml_property_copy(xml_agent, "class", class); set_xml_property_copy(xml_agent, XML_ATTR_TYPE, type); /* ra_data = g_hashtable_lookup(metadata, type); */ if(ra_data != NULL) { xml_metadata = create_xml_node( xml_metadata, "agent_metadata"); set_xml_property_copy( xml_metadata, "class", class); set_xml_property_copy( xml_metadata, XML_ATTR_TYPE, type); tmp = string2xml(ra_data); if(tmp != NULL) { xmlAddChild(xml_metadata, tmp); } /* extract version */ } set_xml_property_copy(xml_agent, "version", version); ) g_list_free(types); ); g_list_free(classes); return TRUE; } gboolean stop_all_resources(void) { GList *op_list = NULL; GList *lrm_list = NULL; state_flag_t cur_state = 0; const char *this_op = NULL; lrm_list = fsa_lrm_conn->lrm_ops->get_all_rscs(fsa_lrm_conn); slist_iter( rid, char, lrm_list, lpc, /* GHashTable* params; */ lrm_rsc_t *the_rsc = fsa_lrm_conn->lrm_ops->get_rsc(fsa_lrm_conn, rid); crm_info("Processing lrm_rsc_t entry %s", rid); if(the_rsc == NULL) { crm_err("NULL resource returned from the LRM"); continue; } op_list = the_rsc->ops->get_cur_state(the_rsc, &cur_state); crm_verbose("\tcurrent state:%s\n", cur_state==LRM_RSC_IDLE?"Idle":"Busy"); slist_iter( op, lrm_op_t, op_list, llpc, this_op = op->op_type; crm_debug("Processing op %s for %s (status=%d, rc=%d)", op->op_type, the_rsc->id, op->op_status, op->rc); if(safe_str_neq(this_op, CRMD_RSCSTATE_STOP)){ do_lrm_rsc_op(the_rsc, the_rsc->id, CRMD_RSCSTATE_STOP, NULL); } break; ); ); return TRUE; } gboolean build_active_RAs(xmlNodePtr rsc_list) { GList *op_list = NULL; GList *lrm_list = NULL; gboolean found_op = FALSE; state_flag_t cur_state = 0; const char *this_op = NULL; char *tmp = NULL; lrm_list = fsa_lrm_conn->lrm_ops->get_all_rscs(fsa_lrm_conn); slist_iter( rid, char, lrm_list, lpc, /* GHashTable* params; */ lrm_rsc_t *the_rsc = fsa_lrm_conn->lrm_ops->get_rsc(fsa_lrm_conn, rid); xmlNodePtr xml_rsc = create_xml_node( rsc_list, XML_LRM_TAG_RESOURCE); crm_info("Processing lrm_rsc_t entry %s", rid); if(the_rsc == NULL) { crm_err("NULL resource returned from the LRM"); continue; } set_xml_property_copy(xml_rsc, XML_ATTR_ID, the_rsc->id); set_xml_property_copy( xml_rsc, XML_LRM_ATTR_TARGET, fsa_our_uname); op_list = the_rsc->ops->get_cur_state(the_rsc, &cur_state); crm_verbose("\tcurrent state:%s\n", cur_state==LRM_RSC_IDLE?"Idle":"Busy"); slist_iter( op, lrm_op_t, op_list, llpc, this_op = op->op_type; crm_debug("Processing op %s for %s (status=%d, rc=%d)", op->op_type, the_rsc->id, op->op_status, op->rc); if(op->rc != 0 || safe_str_neq(this_op, CRMD_RSCSTATE_MON)){ set_xml_property_copy( xml_rsc, XML_LRM_ATTR_RSCSTATE, op->user_data); set_xml_property_copy( xml_rsc, XML_LRM_ATTR_LASTOP, this_op); tmp = crm_itoa(op->rc); set_xml_property_copy( xml_rsc, XML_LRM_ATTR_RC, tmp); crm_free(tmp); tmp = crm_itoa(op->op_status); set_xml_property_copy( xml_rsc, XML_LRM_ATTR_OPSTATUS, tmp); crm_free(tmp); /* we only want the last one */ found_op = TRUE; break; } else { set_xml_property_copy( xml_rsc, XML_LRM_ATTR_RSCSTATE, CRMD_RSCSTATE_START_OK); set_xml_property_copy( xml_rsc, XML_LRM_ATTR_LASTOP, CRMD_RSCSTATE_START); tmp = crm_itoa(op->rc); set_xml_property_copy( xml_rsc, XML_LRM_ATTR_RC, tmp); crm_free(tmp); set_xml_property_copy( xml_rsc, XML_LRM_ATTR_OPSTATUS, "0"); /* we only want the last one */ found_op = TRUE; break; } ); if(found_op == FALSE) { 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); ); g_list_free(lrm_list); return TRUE; } xmlNodePtr do_lrm_query(gboolean is_replace) { xmlNodePtr xml_result= NULL; xmlNodePtr xml_state = create_xml_node(NULL, XML_CIB_TAG_STATE); xmlNodePtr xml_data = create_xml_node(xml_state, XML_CIB_TAG_LRM); xmlNodePtr rsc_list = create_xml_node(xml_data,XML_LRM_TAG_RESOURCES); xmlNodePtr xml_agent_list = create_xml_node(xml_data, "lrm_agents"); xmlNodePtr xml_metadata_list = create_xml_node(xml_data, "metatdata"); /* Build a list of supported agents and metadata */ build_suppported_RAs(xml_metadata_list, xml_agent_list); /* Build a list of active (not always running) resources */ build_active_RAs(rsc_list); if(is_replace) { set_xml_property_copy(xml_state, "replace", XML_CIB_TAG_LRM); } set_uuid(xml_state, XML_ATTR_UUID, fsa_our_uname); set_xml_property_copy(xml_state, XML_ATTR_UNAME, fsa_our_uname); xml_result = create_cib_fragment(xml_state, NULL); crm_xml_debug(xml_state, "Current state of the LRM"); return xml_result; } /* A_LRM_INVOKE */ enum crmd_fsa_input 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) { enum crmd_fsa_input next_input = I_NULL; xmlNodePtr msg; const char *operation = NULL; char rid[64]; const char *id_from_cib = NULL; const char *crm_op = NULL; lrm_rsc_t *rsc = NULL; msg = (xmlNodePtr)msg_data->data; operation = get_xml_attr_nested( msg, rsc_path, DIMOF(rsc_path) -3, XML_LRM_ATTR_TASK, TRUE); /* xmlNodePtr tmp = find_xml_node_nested(msg, rsc_path, DIMOF(rsc_path) -3); */ /* operation = xmlGetProp(tmp, XML_LRM_ATTR_TASK); */ if(operation == NULL) { crm_err("No value for %s in message at level %d.", XML_LRM_ATTR_TASK, DIMOF(rsc_path) -3); return I_NULL; } id_from_cib = get_xml_attr_nested( msg, rsc_path, DIMOF(rsc_path) -2, XML_ATTR_ID, TRUE); if(id_from_cib == NULL) { crm_err("No value for %s in message at level %d.", XML_ATTR_ID, DIMOF(rsc_path) -2); return I_NULL; } /* only the first 16 chars are used by the LRM */ strncpy(rid, id_from_cib, 64); rid[63] = 0; crm_op = get_xml_attr(msg, XML_TAG_OPTIONS, XML_ATTR_OP, TRUE); rsc = fsa_lrm_conn->lrm_ops->get_rsc(fsa_lrm_conn, rid); if(crm_op != NULL && safe_str_eq(crm_op, "lrm_query")) { xmlNodePtr data, reply; data = do_lrm_query(FALSE); reply = create_reply(msg, data); relay_message(reply, TRUE); free_xml(data); free_xml(reply); } else if(operation != NULL) { next_input = do_lrm_rsc_op(rsc, rid, operation, msg); } else { next_input = I_ERROR; } return next_input; } enum crmd_fsa_input do_lrm_rsc_op( lrm_rsc_t *rsc, char *rid, const char *operation, xmlNodePtr msg) { lrm_op_t* op = NULL; int call_id = 0; int action_timeout = 0; const char *class = get_xml_attr_nested( msg, rsc_path, DIMOF(rsc_path) -2, "class", TRUE); const char *type = get_xml_attr_nested( msg, rsc_path, DIMOF(rsc_path) -2, XML_ATTR_TYPE, TRUE); const char *timeout = get_xml_attr_nested( msg, rsc_path, DIMOF(rsc_path) -2, "timeout", FALSE); if(rsc == NULL) { /* add it to the list */ crm_verbose("adding rsc %s before operation", rid); fsa_lrm_conn->lrm_ops->add_rsc( fsa_lrm_conn, rid, class, type, NULL, NULL); rsc = fsa_lrm_conn->lrm_ops->get_rsc(fsa_lrm_conn, rid); } if(rsc == NULL) { crm_err("Could not add resource to LRM"); return I_FAIL; } if(timeout) { action_timeout = atoi(timeout); if(action_timeout < 0) { action_timeout = 0; } } /* stop the monitor before stopping the resource */ if(safe_str_eq(operation, CRMD_RSCSTATE_STOP)) { gpointer foo = g_hash_table_lookup(monitors, rsc->id); call_id = GPOINTER_TO_INT(foo); if(call_id > 0) { crm_debug("Stopping status op for %s", rsc->id); rsc->ops->cancel_op(rsc, call_id); g_hash_table_remove(monitors, rsc->id); /* TODO: Clean up key */ } else { crm_warn("No monitor operation found for %s", rsc->id); /* TODO: we probably need to look up the LRM to find it */ } } /* now do the op */ crm_info("Performing op %s on %s", operation, rid); crm_malloc(op, sizeof(lrm_op_t)); op->op_type = crm_strdup(operation); op->params = xml2list(msg, rsc_path, DIMOF(rsc_path)); op->timeout = action_timeout; op->interval = 0; op->user_data = NULL; op->target_rc = EVERYTIME; if(safe_str_eq(CRMD_RSCSTATE_START, operation)) { op->user_data = crm_strdup(CRMD_RSCSTATE_START_OK); } else if(safe_str_eq(CRMD_RSCSTATE_STOP, operation)) { op->user_data = crm_strdup(CRMD_RSCSTATE_STOP_OK); } else { crm_warn("Using status \"complete\" for op \"%s\"" "... this is still in the experimental stage.", operation); op->user_data = crm_strdup(CRMD_RSCSTATE_GENERIC_OK); } op->user_data_len = 1+strlen(op->user_data); call_id = rsc->ops->perform_op(rsc, op); free_lrm_op(op); if(call_id <= 0) { crm_err("Operation %s on %s failed", operation, rid); return I_FAIL; } if(safe_str_eq(operation, CRMD_RSCSTATE_START)) { /* initiate the monitor action */ crm_malloc(op, sizeof(lrm_op_t)); op->op_type = crm_strdup(CRMD_RSCSTATE_MON); op->params = NULL; op->user_data = crm_strdup(CRMD_RSCSTATE_MON_OK); op->timeout = 0; op->interval = 9000; op->target_rc = CHANGED; op->user_data_len = 1+strlen(op->user_data); call_id = rsc->ops->perform_op(rsc, op); free_lrm_op(op); if (call_id > 0) { crm_debug("Adding monitor op for %s", rsc->id); g_hash_table_insert( monitors, strdup(rsc->id), GINT_TO_POINTER(call_id)); } else { crm_err("Monitor op for %s did not have a call id", rsc->id); } } return I_NULL; } void free_lrm_op(lrm_op_t *op) { crm_free(op->user_data); crm_free(op->op_type); crm_free(op); } GHashTable * xml2list(xmlNodePtr parent, const char**attr_path, int depth) { xmlNodePtr node_iter = NULL; xmlNodePtr nvpair_list = NULL; GHashTable *nvpair_hash = g_hash_table_new(&g_str_hash, &g_str_equal); if(parent != NULL) { nvpair_list = find_xml_node_nested(parent, attr_path, depth); } while(nvpair_list != NULL){ node_iter = nvpair_list->children; while(node_iter != NULL) { const char *key = xmlGetProp( node_iter, XML_NVPAIR_ATTR_NAME); const char *value = xmlGetProp( node_iter, XML_NVPAIR_ATTR_VALUE); crm_verbose("Added %s=%s", key, value); g_hash_table_insert (nvpair_hash, crm_strdup(key), crm_strdup(value)); node_iter = node_iter->next; } nvpair_list=nvpair_list->next; } return nvpair_hash; } void do_update_resource(lrm_rsc_t *rsc, lrm_op_t* op) { /* */ xmlNodePtr update, iter; char *tmp = NULL; xmlNodePtr fragment; int len = 0; char *fail_state = NULL; - if(op == NULL || rsc == NULL) { crm_err("Either resouce or op was not specified"); return; } + + crm_info("Updating resouce %s after op %s", rsc->id, op->op_type); update = create_xml_node(NULL, XML_CIB_TAG_STATE); set_uuid(update, XML_ATTR_UUID, fsa_our_uname); set_xml_property_copy(update, XML_ATTR_UNAME, fsa_our_uname); iter = create_xml_node(update, XML_CIB_TAG_LRM); iter = create_xml_node(iter, XML_LRM_TAG_RESOURCES); iter = create_xml_node(iter, "lrm_resource"); set_xml_property_copy(iter, XML_ATTR_ID, rsc->id); set_xml_property_copy(iter, XML_LRM_ATTR_LASTOP, op->op_type); len = strlen(op->op_type); len += strlen("_failed_"); crm_malloc(fail_state, sizeof(char)*len); if(fail_state != NULL) { sprintf(fail_state, "%s_failed", op->op_type); } switch(op->op_status) { case LRM_OP_CANCELLED: break; case LRM_OP_ERROR: case LRM_OP_TIMEOUT: case LRM_OP_NOTSUPPORTED: crm_err("An LRM operation failed" " or was aborted"); set_xml_property_copy( iter, XML_LRM_ATTR_RSCSTATE, fail_state); break; case LRM_OP_DONE: set_xml_property_copy( iter, XML_LRM_ATTR_RSCSTATE, op->user_data); break; } crm_free(fail_state); tmp = crm_itoa(op->rc); set_xml_property_copy(iter, XML_LRM_ATTR_RC, tmp); crm_free(tmp); tmp = crm_itoa(op->op_status); set_xml_property_copy(iter, XML_LRM_ATTR_OPSTATUS, tmp); crm_free(tmp); set_xml_property_copy(iter, XML_LRM_ATTR_TARGET, fsa_our_uname); fragment = create_cib_fragment(update, NULL); - - fsa_cib_conn->cmds->modify( - fsa_cib_conn, XML_CIB_TAG_STATUS, fragment, NULL, - cib_discard_reply); + + { + int rc = cib_ok; + rc = fsa_cib_conn->cmds->modify( + fsa_cib_conn, XML_CIB_TAG_STATUS, fragment, NULL, + cib_sync_call); + if(rc != cib_ok) { + crm_err("Resource state update failed: %s", + cib_error2string(rc)); + } + } free_xml(fragment); free_xml(update); } enum crmd_fsa_input 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) { lrm_op_t* op = NULL; lrm_rsc_t* rsc = NULL; if(msg_data->fsa_cause != C_LRM_OP_CALLBACK) { return I_FAIL; } op = (lrm_op_t*)msg_data->data; rsc = op->rsc; - crm_debug("Processing %d event for %s/%s", - op->op_status, op->op_type, rsc->id); + crm_info("Processing %d event for %s/%s", + op->op_status, op->op_type, rsc->id); switch(op->op_status) { case LRM_OP_ERROR: case LRM_OP_CANCELLED: case LRM_OP_TIMEOUT: case LRM_OP_NOTSUPPORTED: crm_err("An LRM operation failed" " or was aborted"); /* fall through */ case LRM_OP_DONE: do_update_resource(rsc, op); break; } return I_NULL; } diff --git a/crm/tengine/tengine.c b/crm/tengine/tengine.c index ea9353c97f..cebc0f073b 100644 --- a/crm/tengine/tengine.c +++ b/crm/tengine/tengine.c @@ -1,604 +1,602 @@ -/* $Id: tengine.c,v 1.36 2004/11/12 17:14:34 andrew Exp $ */ +/* $Id: tengine.c,v 1.37 2004/12/16 14:34:19 andrew Exp $ */ /* * 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 gboolean graph_complete = FALSE; GListPtr graph = NULL; IPC_Channel *crm_ch = NULL; uint transition_timeout = 30*1000; /* 30 seconds */ uint transition_fuzz_timeout = 0; uint default_transition_timeout = 30*1000; /* 30 seconds */ uint next_transition_timeout = 30*1000; /* 30 seconds */ void fire_synapse(synapse_t *synapse); gboolean initiate_action(action_t *action); gboolean confirm_synapse(synapse_t *synapse, int action_id); void process_trigger(int action_id); void check_synapse_triggers(synapse_t *synapse, int action_id); gboolean in_transition = FALSE; te_timer_t *transition_timer = NULL; te_timer_t *transition_fuzz_timer = NULL; int transition_counter = 0; gboolean initialize_graph(void) { if(transition_timer == NULL) { crm_malloc(transition_timer, sizeof(te_timer_t)); transition_timer->timeout = 10; transition_timer->source_id = -1; transition_timer->reason = timeout_timeout; transition_timer->action = NULL; } else { stop_te_timer(transition_timer); } if(transition_fuzz_timer == NULL) { crm_malloc(transition_fuzz_timer, sizeof(te_timer_t)); transition_fuzz_timer->timeout = 10; transition_fuzz_timer->source_id = -1; transition_fuzz_timer->reason = timeout_fuzz; transition_fuzz_timer->action = NULL; } else { stop_te_timer(transition_fuzz_timer); } while(g_list_length(graph) > 0) { synapse_t *synapse = g_list_nth_data(graph, 0); while(g_list_length(synapse->actions) > 0) { action_t *action = g_list_nth_data(synapse->actions,0); synapse->actions = g_list_remove( synapse->actions, action); if(action->timer->source_id > 0) { crm_debug("Removing timer for action: %d", action->id); g_source_remove(action->timer->source_id); } free_xml(action->xml); crm_free(action->timer); crm_free(action); } while(g_list_length(synapse->inputs) > 0) { action_t *action = g_list_nth_data(synapse->inputs, 0); synapse->inputs = g_list_remove(synapse->inputs, action); free_xml(action->xml); crm_free(action); } graph = g_list_remove(graph, synapse); crm_free(synapse); } graph = NULL; return TRUE; } /* * returns the ID of the action if a match is found * returns -1 if a match was not found * returns -2 if a match was found but the action failed (and was * not allowed to) */ int match_graph_event(action_t *action, xmlNodePtr event) { const char *allow_fail = NULL; const char *this_action = NULL; const char *this_node = NULL; const char *this_rsc = NULL; const char *event_node; const char *event_rsc; const char *rsc_state; const char *event_action; const char *event_rc; const char *op_status; action_t *match = NULL; int op_status_i = -3; if(event == NULL) { crm_trace("Ignoring NULL event"); return -1; } event_node = xmlGetProp(event, XML_LRM_ATTR_TARGET); event_action = xmlGetProp(event, XML_LRM_ATTR_LASTOP); event_rsc = xmlGetProp(event, XML_ATTR_ID); event_rc = xmlGetProp(event, XML_LRM_ATTR_RC); rsc_state = xmlGetProp(event, XML_LRM_ATTR_RSCSTATE); op_status = xmlGetProp(event, XML_LRM_ATTR_OPSTATUS); if(op_status != NULL) { op_status_i = atoi(op_status); } this_action = xmlGetProp(action->xml, XML_LRM_ATTR_TASK); this_node = xmlGetProp(action->xml, XML_LRM_ATTR_TARGET); this_rsc = xmlGetProp(action->xml, XML_LRM_ATTR_RSCID); crm_devel("matching against: <%s task=%s node=%s rsc_id=%s/>", action->xml->name, this_action, this_node, this_rsc); if(safe_str_neq(this_node, event_node)) { crm_devel("node mismatch: %s", event_node); } else if(safe_str_neq(this_action, event_action)) { crm_devel("action mismatch: %s", event_action); } else if(safe_str_eq(action->xml->name, "rsc_op")) { crm_devel("rsc_op"); if(safe_str_eq(this_rsc, event_rsc)) { match = action; } else { crm_devel("bad rsc (%s) != (%s)", this_rsc, event_rsc); } } else if(safe_str_eq(action->xml->name, "crm_event")) { crm_devel("crm_event"); match = action; } else { crm_devel("no match"); } if(match == NULL) { crm_debug("didnt match current action"); return -1; } crm_debug("matched"); /* stop this event's timer if it had one */ stop_te_timer(match->timer); /* Process OP status */ allow_fail = xmlGetProp(match->xml, "allow_fail"); switch(op_status_i) { case LRM_OP_DONE: break; case LRM_OP_ERROR: case LRM_OP_TIMEOUT: case LRM_OP_NOTSUPPORTED: if(safe_str_neq(allow_fail, XML_BOOLEAN_TRUE)) { crm_err("Action %s to %s on %s resulted in" " failure... aborting transition.", event_action, event_rsc, event_node); send_abort("Action failed", match->xml); return -2; } break; case LRM_OP_CANCELLED: /* do nothing?? */ crm_warn("Dont know what to do for cancelled ops yet"); break; default: crm_err("Unsupported action result: %d", op_status_i); send_abort("Unsupport action result", match->xml); return -2; } crm_devel("Action %d was successful, looking for next action", match->id); match->complete = TRUE; return match->id; } gboolean process_graph_event(xmlNodePtr event) { int action_id = -1; int op_status_i = 0; const char *op_status = xmlGetProp(event, XML_LRM_ATTR_OPSTATUS); next_transition_timeout = transition_timeout; if(op_status != NULL) { op_status_i = atoi(op_status); } if(op_status_i == -1) { /* just information that the action was sent */ crm_trace("Ignoring TE initiated updates"); return TRUE; } slist_iter( synapse, synapse_t, graph, lpc, /* lookup event */ slist_iter( action, action_t, synapse->actions, lpc2, action_id = match_graph_event(action, event); if(action_id != -1) { break; } ); if(action_id != -1) { break; } ); if(action_id > -1) { crm_xml_devel(event, "Event found"); } else if(action_id == -2) { crm_xml_info(event, "Event found but failed"); } else if(event != NULL) { /* unexpected event, trigger a pe-recompute */ /* possibly do this only for certain types of actions */ send_abort("Event not matched", event); return FALSE; /* } else { we dont care, a transition is starting */ } process_trigger(action_id); if(graph_complete) { /* allow some slack until we are pretty sure nothing * else is happening */ crm_info("Transition complete"); print_state(TRUE); if(transition_fuzz_timer->timeout > 0) { crm_info("Allowing the system to stabilize for %d ms" " before S_IDLE transition", transition_fuzz_timer->timeout); start_te_timer(transition_fuzz_timer); } else { send_success("complete"); } } else { /* restart the transition timer again */ crm_info("Transition not yet complete"); print_state(TRUE); transition_timer->timeout = next_transition_timeout; start_te_timer(transition_timer); } return TRUE; } gboolean initiate_action(action_t *action) { gboolean ret = FALSE; xmlNodePtr options = NULL; xmlNodePtr data = NULL; const char *on_node = NULL; const char *id = NULL; const char *runnable = NULL; const char *optional = NULL; const char *task = NULL; const char *discard = NULL; const char *timeout = NULL; const char *destination = NULL; #ifndef TESTING xmlNodePtr rsc_op = NULL; #endif discard = xmlGetProp(action->xml, XML_LRM_ATTR_DISCARD); on_node = xmlGetProp(action->xml, XML_LRM_ATTR_TARGET); id = xmlGetProp(action->xml, XML_ATTR_ID); runnable = xmlGetProp(action->xml, XML_LRM_ATTR_RUNNABLE); optional = xmlGetProp(action->xml, XML_LRM_ATTR_OPTIONAL); task = xmlGetProp(action->xml, XML_LRM_ATTR_TASK); timeout = xmlGetProp(action->xml, "timeout"); if(id == NULL || strlen(id) == 0 || task == NULL || strlen(task) == 0) { /* error */ crm_err("Failed on corrupted command: %s (id=%s) %s", action->xml->name, crm_str(id), crm_str(task)); } else if(action->type == action_type_pseudo){ crm_info("Executing pseudo-event (%d): " "%s on %s", action->id, task, on_node); action->complete = TRUE; process_trigger(action->id); ret = TRUE; } else if(on_node == NULL || strlen(on_node) == 0) { /* error */ crm_err("Failed on corrupted command: %s (id=%s) %s on %s", action->xml->name, crm_str(id), crm_str(task), crm_str(on_node)); } else if(action->type == action_type_crm){ /* */ crm_info("Executing crm-event (%s): %s on %s", id, task, on_node); #ifndef TESTING data = NULL; action->complete = TRUE; destination = CRM_SYSTEM_CRMD; options = create_xml_node(NULL, XML_TAG_OPTIONS); set_xml_property_copy(options, XML_ATTR_OP, task); #endif ret = TRUE; } else if(action->type == action_type_rsc){ crm_info("Executing rsc-op (%s): %s %s on %s", id, task, xmlGetProp(action->xml->children, XML_ATTR_ID), on_node); /* let everyone know this was invoked */ do_update_cib(action->xml, -1); #ifndef TESTING /* ... */ data = create_xml_node(NULL, "msg_data"); rsc_op = create_xml_node(data, "rsc_op"); options = create_xml_node(NULL, XML_TAG_OPTIONS); set_xml_property_copy(options, XML_ATTR_OP, "rsc_op"); set_xml_property_copy(rsc_op, XML_ATTR_ID, id); set_xml_property_copy(rsc_op, XML_LRM_ATTR_TASK, task); set_xml_property_copy(rsc_op, XML_LRM_ATTR_TARGET, on_node); add_node_copy(rsc_op, action->xml->children); destination = CRM_SYSTEM_LRMD; - - #endif ret = TRUE; } else { crm_err("Failed on unsupported command type: " "%s, %s (id=%s) on %s", action->xml->name, task, id, on_node); } if(ret && options != NULL) { char *counter = crm_itoa(transition_counter); set_xml_property_copy( options, "transition_id", crm_str(counter)); crm_free(counter); crm_xml_debug(options, "Performing"); if(data != NULL) { crm_xml_debug(data, "Performing"); } #ifdef MSG_LOG if(msg_te_strm != NULL) { char *message = dump_xml_formatted(data); char *ops = dump_xml_formatted(options); fprintf(msg_te_strm, "[Action]\t%s\n%s\n", crm_str(ops), crm_str(message)); fflush(msg_te_strm); crm_free(message); crm_free(ops); } #endif send_ipc_request( crm_ch, options, data, on_node, destination, CRM_SYSTEM_TENGINE, NULL, NULL); if(action->timeout > 0) { crm_debug("Setting timer for action %d",action->id); start_te_timer(action->timer); } } free_xml(options); free_xml(data); return ret; } gboolean initiate_transition(void) { crm_info("Initating transition"); process_graph_event(NULL); return TRUE; } void check_synapse_triggers(synapse_t *synapse, int action_id) { synapse->triggers_complete = TRUE; if(synapse->confirmed) { crm_debug("Skipping confirmed synapse %d", synapse->id); return; } else if(synapse->complete == FALSE) { crm_debug("Checking pre-reqs for %d", synapse->id); /* lookup prereqs */ slist_iter( prereq, action_t, synapse->inputs, lpc, crm_devel("Processing input %d", prereq->id); if(prereq->id == action_id) { crm_devel("Marking input %d complete", action_id); prereq->complete = TRUE; } else if(prereq->complete == FALSE) { crm_devel("Inputs for synapse %d not satisfied", synapse->id); synapse->triggers_complete = FALSE; } ); } } void fire_synapse(synapse_t *synapse) { if(synapse == NULL) { crm_err("Synapse was NULL!"); return; } crm_debug("Checking if synapse %d needs to be fired", synapse->id); if(synapse->complete) { crm_debug("Skipping complete synapse %d", synapse->id); return; } else if(synapse->triggers_complete == FALSE) { crm_debug("Synapse %d not yet satisfied", synapse->id); return; } crm_devel("All inputs for synapse %d satisfied... invoking actions", synapse->id); synapse->complete = TRUE; slist_iter( action, action_t, synapse->actions, lpc, /* allow some leway */ int tmp_time = 2 * action->timeout; gboolean passed = FALSE; action->invoked = TRUE; /* Invoke the action and start the timer */ passed = initiate_action(action); if(passed == FALSE) { crm_err("Failed initiating <%s id=%d> in synapse %d", action->xml->name, action->id, synapse->id); send_abort("Action init failed", action->xml); return; } if(tmp_time > next_transition_timeout) { next_transition_timeout = tmp_time; } ); crm_debug("Synapse %d complete", synapse->id); } gboolean confirm_synapse(synapse_t *synapse, int action_id) { gboolean complete = TRUE; synapse->confirmed = TRUE; slist_iter( action, action_t, synapse->actions, lpc, if(action->type == action_type_rsc && action->complete == FALSE) { complete = FALSE; synapse->confirmed = FALSE; crm_debug("Found an incomplete action" " - transition not complete"); break; } ); return complete; } void process_trigger(int action_id) { graph_complete = TRUE; crm_debug("Processing trigger from action %d", action_id); /* something happened, stop the timer and start it again at the end */ stop_te_timer(transition_timer); slist_iter( synapse, synapse_t, graph, lpc, if(synapse->confirmed) { crm_debug("Skipping confirmed synapse %d", synapse->id); continue; } check_synapse_triggers(synapse, action_id); fire_synapse(synapse); if(graph == NULL) { crm_err("Trigger processing aborted after failed synapse"); break; } crm_debug("Checking if %d is confirmed", synapse->id); if(synapse->complete == FALSE) { crm_debug("Found an incomplete synapse" " - transition not complete"); /* indicate that the transition is not yet complete */ graph_complete = FALSE; } else if(synapse->confirmed == FALSE) { graph_complete = graph_complete && confirm_synapse(synapse, action_id); } crm_debug("%d is %s", synapse->id, synapse->confirmed?"confirmed":synapse->complete?"complete":"pending"); ); } diff --git a/crm/test/2node-fail.sh.in b/crm/test/2node-fail.sh.in index 6253cd869e..b3995c8575 100644 --- a/crm/test/2node-fail.sh.in +++ b/crm/test/2node-fail.sh.in @@ -1,339 +1,341 @@ #!/bin/bash # # Copyright (C) 2004 Andrew Beekhof # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program 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 program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # testdir=@libdir@/heartbeat/crmtest . ${testdir}/helper.sh || exit 1 CRM_ERR_SHUTDOWN=0 test_nodes=2 function 2node_fail_test() { test_type=$1 fail_pieces=$2 fail_node=$3 good_node=$4 do_failback=$5 accelerated=$6 if [ $fail_node = $test_node_1 ]; then moved_rsc=rsc1 else moved_rsc=rsc2 fi #---- echo -ne "\033]0;$test_type: Iteration $iteration of $repeats\007" crm-cleanup do_cmd echo "#############################" do_cmd echo "$test_type: Iteration $iteration of $repeats" # make *sure* theres nothing left over from last time #---- do_cmd echo "wait for HA to start on ${test_node_1}" crm_log_pos=$(stat -L -c %s $logfile) do_cmd remote_cmd $INIT_USER $test_node_1 $HALIB_DIR/heartbeat -M "2>&1 >/dev/null" & - do_cmd ${testdir}/testutils.pl -p $crm_log_pos -l ${logfile} --search -a -m 500 \ + do_cmd ${testdir}/testutils.pl -p $crm_log_pos -l ${logfile} --search -a -m 1000 \ -s "${test_node_1} ccm(.*): info: Hostname: ${test_node_1}" \ -s "${test_node_1} heartbeat(.*) info: Starting(.*)lrmd" \ -e "${test_node_1} heartbeat(.*)Client(.*) respawning too fast" cts_assert "Startup of Heartbeat on ${test_node_1} failed." #---- do_cmd echo "wait for CRMd to start on ${test_node_1}" crm_log_pos=$(stat -L -c %s $logfile) do_cmd remote_cmd $CRMD_USER $test_node_1 $HALIB_DIR/crmd "$CRM_OPTS" "2>&1 >/dev/null" & - do_cmd ${testdir}/testutils.pl -p $crm_log_pos -l ${logfile} --search -a -m 1500 \ + do_cmd ${testdir}/testutils.pl -p $crm_log_pos -l ${logfile} --search -a -m 2500 \ -s "${test_node_1} crmd(.*): info:(.*)FSA Hostname: ${test_node_1}" \ -s "crmd(.*) State transition (.*) \-> \"S_IDLE\"" cts_assert "CRMd startup on ${test_node_1} failed." do_cmd wait_for_state S_IDLE 3 $test_node_1 cts_assert "S_IDLE not reached on $test_node_1 (startup)!" #---- do_cmd echo Create the first constraint and wait for S_IDLE rsc=rsc1 uuid1=`uuidgen` uuid2=`uuidgen` uuid3=`uuidgen` node_xml="' '" crm_log_pos=$(stat -L -c %s $logfile) do_cmd make_constraint_adv $test_node_1 $node_xml do_cmd ${testdir}/testutils.pl -p $crm_log_pos -l ${logfile} --search -a -m 1500 \ -s "crmd(.*) State transition (.*) \-> \"S_IDLE\"" cts_assert Adding constraint1 did not pass #---- do_cmd echo Create the second constraint and wait for S_IDLE rsc=rsc2 uuid1=`uuidgen` uuid2=`uuidgen` uuid3=`uuidgen` node_xml="' '" crm_log_pos=$(stat -L -c %s $logfile) do_cmd make_constraint_adv $test_node_1 $node_xml - do_cmd ${testdir}/testutils.pl -p $crm_log_pos -l ${logfile} --search -a -m 1500 \ + do_cmd ${testdir}/testutils.pl -p $crm_log_pos -l ${logfile} --search -a -m 2500 \ -s "crmd(.*) State transition (.*) \-> \"S_IDLE\"" cts_assert Adding constraint2 did not pass #---- do_cmd echo Create the first resource and wait for S_IDLE after start args="" crm_log_pos=$(stat -L -c %s $logfile) do_cmd make_resource $test_node_1 rsc1 heartbeat IPaddr - - ignore $args - do_cmd ${testdir}/testutils.pl -p $crm_log_pos -l ${logfile} --search -a -m 1500 \ + do_cmd ${testdir}/testutils.pl -p $crm_log_pos -l ${logfile} --search -a -m 2500 \ -s "crmd(.*) State transition (.*) \-> \"S_IDLE\"" \ -s "crmd(.*) Performing op start(.*) on rsc1" \ -s "crmd(.*) Resource state: rsc1(.*) after start" cts_assert Adding rsc1 did not pass #---- do_cmd echo Create the second resource and wait for S_IDLE after start args="" crm_log_pos=$(stat -L -c %s $logfile) do_cmd make_resource $test_node_1 rsc2 heartbeat IPaddr - - ignore $args - do_cmd ${testdir}/testutils.pl -p $crm_log_pos -l ${logfile} --search -a -m 1500 \ + do_cmd ${testdir}/testutils.pl -p $crm_log_pos -l ${logfile} --search -a -m 2500 \ -s "crmd(.*) State transition (.*) \-> \"S_IDLE\"" \ -s "crmd(.*) Performing op start(.*) on rsc2" \ -s "crmd(.*) Resource state: rsc2(.*) after start" cts_assert Adding rsc2 did not pass #---- do_cmd echo Various sanity checks - stage 1 do_cmd wait_for_state S_IDLE 3 $test_node_1 cts_assert "S_IDLE not reached on $test_node_1 (CIB create)!" do_cmd is_running rsc1 $test_node_1 cts_assert "rsc1 NOT running" do_cmd is_running rsc2 $test_node_1 cts_assert "rsc2 NOT running" do_cmd is_dc $test_node_1 cts_assert "$test_node_1 is supposed to be the DC" do_cmd is_running rsc1 $test_node_1 x$test_node_1 cts_assert_false "rsc1 IS running on x$test_node_1" do_cmd is_running rsc1 $test_node_1 $test_node_1 cts_assert "rsc1 NOT running on $test_node_1" do_cmd is_running rsc2 $test_node_1 $test_node_1 cts_assert "rsc2 NOT running on $test_node_1" #---- do_cmd echo "wait for HA to start on $test_node_2" crm_log_pos=$(stat -L -c %s $logfile) do_cmd remote_cmd $INIT_USER $test_node_2 $HALIB_DIR/heartbeat -M "2>&1 >/dev/null" & - do_cmd ${testdir}/testutils.pl -p $crm_log_pos -l ${logfile} --search -a -m 1500 \ + do_cmd ${testdir}/testutils.pl -p $crm_log_pos -l ${logfile} --search -a -m 5000 \ -s "${test_node_2} ccm(.*) Hostname: ${test_node_2}" \ -s "${test_node_2} heartbeat(.*): info: Starting (.*)lrmd" \ -e "${test_node_2} heartbeat(.*) Client (.*) respawning too fast" cts_assert "Startup of Heartbeat on ${test_node_2} failed." #---- do_cmd echo "wait for CRMd to start on $test_node_2" crm_log_pos=$(stat -L -c %s $logfile) do_cmd remote_cmd $CRMD_USER $test_node_2 $HALIB_DIR/crmd "$CRM_OPTS" "2>&1 >/dev/null" & - do_cmd ${testdir}/testutils.pl -p $crm_log_pos -l ${logfile} --search -a -m 2500 \ + do_cmd ${testdir}/testutils.pl -p $crm_log_pos -l ${logfile} --search -a -m 5000 \ -s "${test_node_2} crmd(.*)FSA Hostname: ${test_node_2}" \ -s "${test_node_2} crmd(.*) State transition \"S_PENDING\" \-> \"S_NOT_DC\"" cts_assert "CRMd startup on ${test_node_2} failed." - do_cmd ${testdir}/testutils.pl -p $crm_log_pos -l ${logfile} --search -a -m 3500 \ + do_cmd ${testdir}/testutils.pl -p $crm_log_pos -l ${logfile} --search -a -m 8000 \ -s "${test_node_1} crmd(.*) State transition(.*) \-> \"S_IDLE\"" \ -s "${test_node_2} crmd(.*) Performing op start(.*) on rsc2" \ -s "crmd(.*) Resource state: rsc2(.*) after start] on ${test_node_2}" cts_assert "rsc2 was not transferred to ${test_node_2} on startup." #---- do_cmd echo Various sanity checks - stage 2 do_cmd wait_for_state S_NOT_DC 30 $test_node_2 cts_assert "S_NOT_DC not reached on $test_node_2 (startup - 2)!" do_cmd wait_for_state S_IDLE 30 $test_node_1 cts_assert "S_IDLE not reached on $test_node_1 (startup - 2)!" do_cmd is_running rsc1 $test_node_1 cts_assert "rsc1 NOT running" do_cmd is_running rsc2 $test_node_1 cts_assert "rsc2 NOT running" do_cmd is_running rsc1 $test_node_1 $test_node_1 cts_assert "rsc1 NOT running on $test_node_1" do_cmd is_running rsc2 $test_node_1 $test_node_2 cts_assert "rsc2 NOT running on $test_node_2" #---- is_dc $fail_node 2>&1 > /dev/null test_for_election=$? do_cmd echo Killing $fail_pieces on $fail_node crm_log_pos=$(stat -L -c %s $logfile) do_cmd remote_cmd $ADMIN_USER $fail_node "killall -9 $fail_pieces" & if [ $test_for_election = 0 ]; then do_cmd echo Killed the DC... checking for DC Failover - do_cmd ${testdir}/testutils.pl -p $crm_log_pos -l ${logfile} --search -a -m 60000 \ + do_cmd ${testdir}/testutils.pl -p $crm_log_pos -l ${logfile} --search -a -m 80000 \ + -s "${good_node} crmd(.*) State transition" \ -s "${good_node} crmd(.*) State transition (.*) \-> \"S_ELECTION\"" \ -s "${good_node} crmd(.*) State transition (.*) \-> \"S_IDLE\"" cts_assert "Transition of the DC from ${fail_node} to ${good_node} failed." else do_cmd echo Killed slave node... checking the DC noticed - do_cmd ${testdir}/testutils.pl -p $crm_log_pos -l ${logfile} --search -a -m 60000 \ + do_cmd ${testdir}/testutils.pl -p $crm_log_pos -l ${logfile} --search -a -m 80000 \ + -s "${good_node} crmd(.*) State transition" \ -s "${good_node} crmd(.*) State transition (.*) \-> \"S_POLICY_ENGINE\"" \ -s "${good_node} crmd(.*) State transition (.*) \-> \"S_IDLE\"" cts_assert "Failure of slave node ${fail_node} was not noticed on the DC (${good_node})." fi - do_cmd ${testdir}/testutils.pl -p $crm_log_pos -l ${logfile} --search -a -m 60000 \ + do_cmd ${testdir}/testutils.pl -p $crm_log_pos -l ${logfile} --search -a -m 80000 \ -s "${god_node} crmd(.*) Performing op start(.*) on ${moved_rsc}" \ -s "crmd(.*) Resource state: ${moved_rsc}(.*) after start] on ${good_node}" cts_assert "Move of ${moved_rsc} to ${good_node} failed." #---- do_cmd echo Various sanity checks - stage 3 do_cmd wait_for_state S_IDLE 60 $good_node cts_assert "S_IDLE not reached on $good_node after kill!" do_cmd is_running rsc1 $good_node cts_assert "rsc1 NOT running on $good_node" do_cmd is_running rsc2 $good_node cts_assert "rsc2 NOT running on $good_node" do_cmd is_running rsc1 $good_node $fail_node cts_assert_false "rsc1 IS running on $fail_node" do_cmd is_running rsc2 $good_node $good_node cts_assert "rsc2 NOT running on $good_node" #---- if [ $do_failback = 1 ]; then do_cmd echo "Re-Starting on failed node $fail_node" if [ "$fail_pieces" = "crmd" ]; then do_cmd echo "HA still running, skipping restart" else do_cmd echo "wait for HA to start on $fail_node" crm_log_pos=$(stat -L -c %s $logfile) do_cmd remote_cmd $INIT_USER $fail_node $HALIB_DIR/heartbeat -M "2>&1 >/dev/null" & - do_cmd ${testdir}/testutils.pl -p $crm_log_pos -l ${logfile} --search -a -m 2000 \ + do_cmd ${testdir}/testutils.pl -p $crm_log_pos -l ${logfile} --search -a -m 5000 \ -s "${fail_node} ccm(.*) Hostname: ${fail_node}" \ -s "${fail_node} heartbeat(.*): info: Starting (.*)lrmd" \ -e "${fail_node} heartbeat(.*) Client (.*) respawning too fast" cts_assert "Startup of Heartbeat on ${fail_node} failed." fi #---- do_cmd echo "wait for CRMd to start on $fail_node" crm_log_pos=$(stat -L -c %s $logfile) do_cmd remote_cmd $CRMD_USER $fail_node $HALIB_DIR/crmd "$CRM_OPTS" "2>&1 >/dev/null" & - do_cmd ${testdir}/testutils.pl -p $crm_log_pos -l ${logfile} --search -a -m 10000 \ + do_cmd ${testdir}/testutils.pl -p $crm_log_pos -l ${logfile} --search -a -m 20000 \ -s "${fail_node} crmd(.*)FSA Hostname: ${fail_node}" \ -s "${fail_node} crmd(.*) State transition \"S_PENDING\" \-> \"S_NOT_DC\"" cts_assert "CRMd startup on ${fail_node} failed." - do_cmd ${testdir}/testutils.pl -p $crm_log_pos -l ${logfile} --search -a -m 10000 \ + do_cmd ${testdir}/testutils.pl -p $crm_log_pos -l ${logfile} --search -a -m 20000 \ -s "${good_node} crmd(.*) State transition(.*) \-> \"S_IDLE\"" \ -s "${fail_node} crmd(.*) Performing op start(.*) on ${moved_rsc}" \ -s "crmd(.*) Resource state: ${moved_rsc}(.*) after start] on ${fail_node}" cts_assert "$moved_rsc was not transferred to ${fail_node} on startup." do_cmd echo Various sanity checks - stage 4 do_cmd wait_for_state S_NOT_DC 30 $fail_node cts_assert "S_NOT_DC not reached on $fail_node (restart)!" do_cmd wait_for_state S_IDLE 30 $good_node cts_assert "S_IDLE not reached on $good_node (restart)!" do_cmd is_running rsc1 $test_node_1 cts_assert "rsc1 NOT running" do_cmd is_running rsc2 $test_node_1 cts_assert "rsc2 NOT running" do_cmd is_running rsc1 $test_node_1 $test_node_1 cts_assert "rsc1 NOT running on $test_node_1" do_cmd is_running rsc2 $test_node_1 $test_node_2 cts_assert "rsc2 NOT running on $test_node_2" fi #---- do_cmd echo "test ${test_type}: PASSED" } while [ $iteration -lt $repeats ]; do iteration=`expr $iteration + 1` echo -ne "\033]0;$test_type : Iteration $iteration of $repeats\007" echo "########### $test_type : Begining iteration $iteration of $repeats ###########" ( 2node_fail_test 2node__fail_DC_All "heartbeat ccm lrmd crmd" ${test_node_1} ${test_node_2} 0 0 2node_fail_test 2node__fail_slave_All "heartbeat ccm lrmd crmd" ${test_node_2} ${test_node_1} 0 0 2node_fail_test 2node__failback_DC_All "heartbeat ccm lrmd crmd" ${test_node_1} ${test_node_2} 1 0 2node_fail_test 2node__failback_slave_All "heartbeat ccm lrmd crmd" ${test_node_2} ${test_node_1} 1 0 2node_fail_test 2node__fail_DC_CRMd "crmd" ${test_node_1} ${test_node_2} 0 0 2node_fail_test 2node__fail_slave_CRMd "crmd" ${test_node_2} ${test_node_1} 0 0 2node_fail_test 2node__failback_DC_CRMd "crmd" ${test_node_1} ${test_node_2} 1 0 2node_fail_test 2node__failback_slave_CRMd "crmd" ${test_node_2} ${test_node_1} 1 0 #2node_fail_test 2node__fail_slave_LRM "lrmd" ${test_node_2} ${test_node_1} #2node_fail_test 2node__fail_DC_LRM "lrmd" ${test_node_1} ${test_node_2} echo "test suite: PASSED" ) done diff --git a/include/crm/cib.h b/include/crm/cib.h index d5095bacc6..6c270aeb37 100644 --- a/include/crm/cib.h +++ b/include/crm/cib.h @@ -1,266 +1,269 @@ -/* $Id: cib.h,v 1.10 2004/12/10 20:07:07 andrew Exp $ */ +/* $Id: cib.h,v 1.11 2004/12/16 14:34:16 andrew Exp $ */ /* * 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 CIB__H #define CIB__H #include #include #include enum cib_variant { cib_native, cib_database, cib_edir }; enum cib_state { cib_connected_command, cib_connected_query, cib_disconnected }; enum cib_conn_type { cib_command, cib_query, cib_no_connection }; enum cib_call_options { cib_none = 0x000000, cib_verbose = 0x000001, cib_discard_reply = 0x000004, cib_scope_local = 0x000010, /* cib_scope_global = 0x000020, */ cib_sync_call = 0x000040, /* cib_async_call = 0x000080, */ cib_inhibit_notify= 0x000100 }; #define cib_default_options = cib_none enum cib_errors { cib_ok = 0, cib_operation = -1, cib_create_msg = -2, cib_not_connected = -3, cib_not_authorized = -4, cib_send_failed = -5, cib_reply_failed = -6, cib_return_code = -7, cib_output_ptr = -8, cib_output_data = -9, cib_connection = -10, cib_authentication = -11, cib_missing = -12, cib_variant = -28, CIBRES_MISSING_ID = -13, CIBRES_MISSING_TYPE = -14, CIBRES_MISSING_FIELD = -15, CIBRES_OBJTYPE_MISMATCH = -16, CIBRES_CORRUPT = -17, CIBRES_OTHER = -18, cib_unknown = -19, cib_STALE = -20, cib_EXISTS = -21, cib_NOTEXISTS = -22, cib_ACTIVATION = -23, cib_NOSECTION = -24, cib_NOOBJECT = -25, cib_NOPARENT = -26, cib_NODECOPY = -27, cib_NOTSUPPORTED = -29, cib_registration_msg = -30, cib_callback_token = -31, cib_callback_register = -32, cib_msg_field_add = -33, cib_client_gone = -34, cib_not_master = -35, - cib_client_corrupt = -36 + cib_client_corrupt = -36, + cib_master_timeout = -37 }; enum cib_op { CIB_OP_NONE = 0, CIB_OP_ADD, CIB_OP_MODIFY, CIB_OP_DELETE, CIB_OP_MAX }; enum cib_section { cib_section_none, cib_section_all, cib_section_nodes, cib_section_constraints, cib_section_resources, cib_section_crmconfig, cib_section_status }; #define F_CIB_CLIENTID "cib_clientid" #define F_CIB_CALLOPTS "cib_callopt" #define F_CIB_CALLID "cib_callid" #define F_CIB_CALLDATA "cib_calldata" #define F_CIB_OPERATION "cib_op" #define F_CIB_ISREPLY "cib_isreplyto" #define F_CIB_SECTION "cib_section" #define F_CIB_HOST "cib_host" #define F_CIB_RC "cib_rc" #define F_CIB_CALLBACK_TOKEN "cib_callback_token" #define F_CIB_GLOBAL_UPDATE "cib_update" #define F_CIB_DELEGATED "cib_delegated_from" #define F_CIB_OBJID "cib_object" #define F_CIB_OBJTYPE "cib_object_type" +#define F_CIB_EXISTING "cib_existing_object" +#define F_CIB_SEENCOUNT "cib_seen" +#define F_CIB_TIMEOUT "cib_timeout" #define F_CIB_UPDATE "cib_update" #define F_CIB_UPDATE_RESULT "cib_update_result" -#define F_CIB_EXISTING "cib_existing_object" #define T_CIB "cib" #define T_CIB_NOTIFY "cib_notify" /* notify sub-types */ #define T_CIB_PRE_NOTIFY "cib_pre_notify" #define T_CIB_POST_NOTIFY "cib_post_notify" #define T_CIB_UPDATE_CONFIRM "cib_update_confirmation" typedef struct cib_s cib_t; typedef struct cib_api_operations_s { int (*variant_op)( cib_t *cib, const char *op, const char *host, const char *section, xmlNodePtr data, xmlNodePtr *output_data, int call_options); int (*signon) (cib_t *cib, enum cib_conn_type type); int (*signoff)(cib_t *cib); int (*free) (cib_t *cib); int (*set_op_callback)( cib_t *cib, void (*callback)( const struct ha_msg *msg, int callid , int rc, xmlNodePtr output)); int (*add_notify_callback)( cib_t *cib, const char *event, void (*callback)( const char *event, struct ha_msg *msg)); int (*del_notify_callback)( cib_t *cib, const char *event, void (*callback)( const char *event, struct ha_msg *msg)); int (*set_connection_dnotify)( cib_t *cib, void (*dnotify)(gpointer user_data)); IPC_Channel *(*channel)(cib_t* cib); int (*inputfd)(cib_t* cib); int (*noop)(cib_t *cib, int call_options); int (*ping)( cib_t *cib, xmlNodePtr *output_data, int call_options); int (*query)(cib_t *cib, const char *section, xmlNodePtr *output_data, int call_options); int (*query_from)( cib_t *cib, const char *host, const char *section, xmlNodePtr *output_data, int call_options); gboolean (*is_master) (cib_t *cib); int (*set_master)(cib_t *cib, int call_options); int (*set_slave) (cib_t *cib, int call_options); int (*set_slave_all)(cib_t *cib, int call_options); int (*sync)(cib_t *cib, const char *section, int call_options); int (*sync_from)( cib_t *cib, const char *host, const char *section, int call_options); int (*bump_epoch)(cib_t *cib, int call_options); int (*create)(cib_t *cib, const char *section, xmlNodePtr data, xmlNodePtr *output_data, int call_options) ; int (*modify)(cib_t *cib, const char *section, xmlNodePtr data, xmlNodePtr *output_data, int call_options) ; int (*replace)(cib_t *cib, const char *section, xmlNodePtr data, xmlNodePtr *output_data, int call_options) ; int (*delete)(cib_t *cib, const char *section, xmlNodePtr data, xmlNodePtr *output_data, int call_options) ; int (*erase)( cib_t *cib, xmlNodePtr *output_data, int call_options); int (*quit)(cib_t *cib, int call_options); gboolean (*msgready)(cib_t* cib); int (*rcvmsg)(cib_t* cib, int blocking); gboolean (*dispatch)(IPC_Channel *channel, gpointer user_data); } cib_api_operations_t; struct cib_s { enum cib_state state; enum cib_conn_type type; int call_id; void *variant_opaque; GList *notify_list; void (*op_callback)(const struct ha_msg *msg, int call_id, int rc, xmlNodePtr output); cib_api_operations_t *cmds; }; typedef struct cib_notify_client_s { const char *event; const char *obj_id; /* implement one day */ const char *obj_type; /* implement one day */ void (*callback)( const char *event, struct ha_msg *msg); } cib_notify_client_t; /* Core functions */ extern cib_t *cib_new(void); extern gboolean startCib(const char *filename); extern xmlNodePtr get_cib_copy(cib_t *cib); extern xmlNodePtr cib_get_generation(cib_t *cib); extern int cib_compare_generation(xmlNodePtr left, xmlNodePtr right); /* Utility functions */ extern xmlNodePtr get_object_root(const char *object_type,xmlNodePtr the_root); extern xmlNodePtr create_cib_fragment_adv( xmlNodePtr update, const char *section, const char *source); extern char *cib_pluralSection(const char *a_section); /* Error Interpretation*/ extern const char *cib_error2string(enum cib_errors); extern const char *cib_op2string(enum cib_op); extern xmlNodePtr createEmptyCib(void); extern gboolean verifyCibXml(xmlNodePtr cib); extern int cib_section2enum(const char *a_section); #define create_cib_fragment(update,section) create_cib_fragment_adv(update, section, __FUNCTION__) #endif diff --git a/lib/crm/cib/cib_client.c b/lib/crm/cib/cib_client.c index 93279683c9..33714c1086 100755 --- a/lib/crm/cib/cib_client.c +++ b/lib/crm/cib/cib_client.c @@ -1,1084 +1,1087 @@ /* * Copyright (c) 2004 International Business Machines * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include gboolean verify_cib_cmds(cib_t *cib); int cib_client_set_op_callback( cib_t *cib, void (*callback)(const struct ha_msg *msg, int call_id, int rc, xmlNodePtr output)); int cib_client_noop(cib_t *cib, int call_options); int cib_client_ping(cib_t *cib, xmlNodePtr *output_data, int call_options); int cib_client_query(cib_t *cib, const char *section, xmlNodePtr *output_data, int call_options); int cib_client_query_from(cib_t *cib, const char *host, const char *section, xmlNodePtr *output_data, int call_options); int cib_client_sync(cib_t *cib, const char *section, int call_options); int cib_client_sync_from( cib_t *cib, const char *host, const char *section, int call_options); gboolean cib_client_is_master(cib_t *cib); int cib_client_set_slave(cib_t *cib, int call_options); int cib_client_set_slave_all(cib_t *cib, int call_options); int cib_client_set_master(cib_t *cib, int call_options); int cib_client_bump_epoch(cib_t *cib, int call_options); int cib_client_create(cib_t *cib, const char *section, xmlNodePtr data, xmlNodePtr *output_data, int call_options) ; int cib_client_modify(cib_t *cib, const char *section, xmlNodePtr data, xmlNodePtr *output_data, int call_options) ; int cib_client_replace(cib_t *cib, const char *section, xmlNodePtr data, xmlNodePtr *output_data, int call_options) ; int cib_client_delete(cib_t *cib, const char *section, xmlNodePtr data, xmlNodePtr *output_data, int call_options) ; int cib_client_erase( cib_t *cib, xmlNodePtr *output_data, int call_options); int cib_client_quit(cib_t *cib, int call_options); int cib_client_add_notify_callback( cib_t *cib, const char *event, void (*callback)( const char *event, struct ha_msg *msg)); int cib_client_del_notify_callback( cib_t *cib, const char *event, void (*callback)( const char *event, struct ha_msg *msg)); -gint cib_GCompareFunc(gconstpointer a, gconstpointer b); +gint ciblib_GCompareFunc(gconstpointer a, gconstpointer b); extern cib_t *cib_native_new(cib_t *cib); static enum cib_variant configured_variant = cib_native; /* define of the api functions*/ cib_t* cib_new(void) { cib_t* new_cib = NULL; if(configured_variant != cib_native) { crm_err("Only the native CIB type is currently implemented"); return NULL; } crm_malloc(new_cib, sizeof(cib_t)); new_cib->call_id = 1; new_cib->type = cib_none; new_cib->state = cib_disconnected; new_cib->op_callback = NULL; new_cib->variant_opaque = NULL; new_cib->notify_list = NULL; /* the rest will get filled in by the variant constructor */ crm_malloc(new_cib->cmds, sizeof(cib_api_operations_t)); memset(new_cib->cmds, 0, sizeof(cib_api_operations_t)); new_cib->cmds->set_op_callback = cib_client_set_op_callback; new_cib->cmds->add_notify_callback = cib_client_add_notify_callback; new_cib->cmds->del_notify_callback = cib_client_del_notify_callback; new_cib->cmds->noop = cib_client_noop; new_cib->cmds->ping = cib_client_ping; new_cib->cmds->query = cib_client_query; new_cib->cmds->sync = cib_client_sync; new_cib->cmds->query_from = cib_client_query_from; new_cib->cmds->sync_from = cib_client_sync_from; new_cib->cmds->is_master = cib_client_is_master; new_cib->cmds->set_master = cib_client_set_master; new_cib->cmds->set_slave = cib_client_set_slave; new_cib->cmds->set_slave_all = cib_client_set_slave_all; new_cib->cmds->bump_epoch = cib_client_bump_epoch; new_cib->cmds->create = cib_client_create; new_cib->cmds->modify = cib_client_modify; new_cib->cmds->replace = cib_client_replace; new_cib->cmds->delete = cib_client_delete; new_cib->cmds->erase = cib_client_erase; new_cib->cmds->quit = cib_client_quit; cib_native_new(new_cib); if(verify_cib_cmds(new_cib) == FALSE) { return NULL; } return new_cib; } int cib_client_set_op_callback( cib_t *cib, void (*callback)(const struct ha_msg *msg, int call_id, int rc, xmlNodePtr output)) { if(callback == NULL) { crm_info("Un-Setting operation callback"); } else { crm_debug("Setting operation callback"); } cib->op_callback = callback; return cib_ok; } int cib_client_noop(cib_t *cib, int call_options) { if(cib == NULL) { return cib_missing; } else if(cib->state == cib_disconnected) { return cib_not_connected; } else if(cib->cmds->variant_op == NULL) { return cib_variant; } return cib->cmds->variant_op( cib, CRM_OP_NOOP, NULL, NULL, NULL, NULL, call_options); } int cib_client_ping(cib_t *cib, xmlNodePtr *output_data, int call_options) { if(cib == NULL) { return cib_missing; } else if(cib->state == cib_disconnected) { return cib_not_connected; } else if(cib->cmds->variant_op == NULL) { return cib_variant; } return cib->cmds->variant_op( cib, CRM_OP_PING, NULL,NULL,NULL, output_data, call_options); } int cib_client_query(cib_t *cib, const char *section, xmlNodePtr *output_data, int call_options) { return cib->cmds->query_from( cib, NULL, section, output_data, call_options); } int cib_client_query_from(cib_t *cib, const char *host, const char *section, xmlNodePtr *output_data, int call_options) { if(cib == NULL) { return cib_missing; } else if(cib->state == cib_disconnected) { return cib_not_connected; } else if(cib->cmds->variant_op == NULL) { return cib_variant; } return cib->cmds->variant_op(cib, CRM_OP_CIB_QUERY, host, section, NULL, output_data, call_options); } gboolean cib_client_is_master(cib_t *cib) { if(cib == NULL) { return cib_missing; } else if(cib->state == cib_disconnected) { return cib_not_connected; } else if(cib->cmds->variant_op == NULL) { return cib_variant; } return cib->cmds->variant_op( cib, CRM_OP_CIB_ISMASTER, NULL, NULL,NULL,NULL, cib_scope_local|cib_sync_call); } int cib_client_set_slave(cib_t *cib, int call_options) { if(cib == NULL) { return cib_missing; } else if(cib->state == cib_disconnected) { return cib_not_connected; } else if(cib->cmds->variant_op == NULL) { return cib_variant; } return cib->cmds->variant_op( cib, CRM_OP_CIB_SLAVE, NULL,NULL,NULL,NULL, call_options); } int cib_client_set_slave_all(cib_t *cib, int call_options) { if(cib == NULL) { return cib_missing; } else if(cib->state == cib_disconnected) { return cib_not_connected; } else if(cib->cmds->variant_op == NULL) { return cib_variant; } return cib->cmds->variant_op( cib, CRM_OP_CIB_SLAVEALL, NULL,NULL,NULL,NULL, call_options); } int cib_client_set_master(cib_t *cib, int call_options) { if(cib == NULL) { return cib_missing; } else if(cib->state == cib_disconnected) { return cib_not_connected; } else if(cib->cmds->variant_op == NULL) { return cib_variant; } crm_debug("Adding cib_scope_local to options"); return cib->cmds->variant_op( cib, CRM_OP_CIB_MASTER, NULL,NULL,NULL,NULL, call_options|cib_scope_local); } int cib_client_bump_epoch(cib_t *cib, int call_options) { if(cib == NULL) { return cib_missing; } else if(cib->state == cib_disconnected) { return cib_not_connected; } else if(cib->cmds->variant_op == NULL) { return cib_variant; } return cib->cmds->variant_op( cib, CRM_OP_CIB_BUMP, NULL, NULL, NULL, NULL, call_options); } int cib_client_sync(cib_t *cib, const char *section, int call_options) { return cib->cmds->sync_from(cib, NULL, section, call_options); } int cib_client_sync_from( cib_t *cib, const char *host, const char *section, int call_options) { enum cib_errors rc = cib_ok; xmlNodePtr stored_cib = NULL; xmlNodePtr current_cib = NULL; if(cib == NULL) { return cib_missing; } else if(cib->state == cib_disconnected) { return cib_not_connected; } else if(cib->cmds->variant_op == NULL) { return cib_variant; } crm_debug("Retrieving current CIB from %s", host); rc = cib->cmds->query_from( cib, host, section, ¤t_cib, call_options|cib_sync_call); if(rc == cib_ok) { if(call_options & cib_scope_local) { /* having scope == local makes no sense from here on */ call_options ^= cib_scope_local; } crm_debug("Storing current CIB (should trigger a store everywhere)"); crm_xml_debug(current_cib, "XML to store"); rc = cib->cmds->replace( cib, section, current_cib, &stored_cib, call_options); } free_xml(current_cib); free_xml(stored_cib); return rc; } int cib_client_create(cib_t *cib, const char *section, xmlNodePtr data, xmlNodePtr *output_data, int call_options) { if(cib == NULL) { return cib_missing; } else if(cib->state == cib_disconnected) { return cib_not_connected; } else if(cib->cmds->variant_op == NULL) { return cib_variant; } return cib->cmds->variant_op(cib, CRM_OP_CIB_CREATE, NULL, section, data, output_data, call_options); } int cib_client_modify(cib_t *cib, const char *section, xmlNodePtr data, xmlNodePtr *output_data, int call_options) { if(cib == NULL) { return cib_missing; } else if(cib->state == cib_disconnected) { return cib_not_connected; } else if(cib->cmds->variant_op == NULL) { return cib_variant; } return cib->cmds->variant_op(cib, CRM_OP_CIB_UPDATE, NULL, section, data, output_data, call_options); } int cib_client_replace(cib_t *cib, const char *section, xmlNodePtr data, xmlNodePtr *output_data, int call_options) { if(cib == NULL) { return cib_missing; } else if(cib->state == cib_disconnected) { return cib_not_connected; } else if(cib->cmds->variant_op == NULL) { return cib_variant; } return cib->cmds->variant_op(cib, CRM_OP_CIB_REPLACE, NULL, NULL, data, output_data, call_options); } int cib_client_delete(cib_t *cib, const char *section, xmlNodePtr data, xmlNodePtr *output_data, int call_options) { if(cib == NULL) { return cib_missing; } else if(cib->state == cib_disconnected) { return cib_not_connected; } else if(cib->cmds->variant_op == NULL) { return cib_variant; } return cib->cmds->variant_op(cib, CRM_OP_CIB_DELETE, NULL, section, data, output_data, call_options); } int cib_client_erase( cib_t *cib, xmlNodePtr *output_data, int call_options) { if(cib == NULL) { return cib_missing; } else if(cib->state == cib_disconnected) { return cib_not_connected; } else if(cib->cmds->variant_op == NULL) { return cib_variant; } return cib->cmds->variant_op(cib, CRM_OP_CIB_ERASE, NULL, NULL, NULL, output_data, call_options); } int cib_client_quit(cib_t *cib, int call_options) { if(cib == NULL) { return cib_missing; } else if(cib->state == cib_disconnected) { return cib_not_connected; } else if(cib->cmds->variant_op == NULL) { return cib_variant; } return cib->cmds->variant_op( cib, CRM_OP_QUIT, NULL, NULL, NULL, NULL, call_options); } int cib_client_add_notify_callback( cib_t *cib, const char *event, void (*callback)( const char *event, struct ha_msg *msg)) { GList *list_item = NULL; cib_notify_client_t *new_client = NULL; crm_debug("Adding callback for %s events (%d)", event, g_list_length(cib->notify_list)); crm_malloc(new_client, sizeof(cib_notify_client_t)); new_client->event = event; new_client->callback = callback; list_item = g_list_find_custom( - cib->notify_list, new_client, cib_GCompareFunc); + cib->notify_list, new_client, ciblib_GCompareFunc); if(list_item != NULL) { crm_warn("Callback already present"); } else { cib->notify_list = g_list_append( cib->notify_list, new_client); crm_debug("Callback added (%d)", g_list_length(cib->notify_list)); } return cib_ok; } int cib_client_del_notify_callback( cib_t *cib, const char *event, void (*callback)( const char *event, struct ha_msg *msg)) { GList *list_item = NULL; cib_notify_client_t *new_client = NULL; crm_debug("Removing callback for %s events", event); crm_malloc(new_client, sizeof(cib_notify_client_t)); new_client->event = event; new_client->callback = callback; list_item = g_list_find_custom( - cib->notify_list, new_client, cib_GCompareFunc); + cib->notify_list, new_client, ciblib_GCompareFunc); if(list_item != NULL) { cib_notify_client_t *list_client = list_item->data; cib->notify_list = g_list_remove(cib->notify_list, list_client); crm_free(list_client); crm_debug("Removed callback"); } else { crm_debug("Callback not present"); } crm_free(new_client); return cib_ok; } -gint cib_GCompareFunc(gconstpointer a, gconstpointer b) +gint ciblib_GCompareFunc(gconstpointer a, gconstpointer b) { const cib_notify_client_t *a_client = a; const cib_notify_client_t *b_client = b; if(a_client->callback == b_client->callback && safe_str_neq(a_client->event, b_client->event)) { return 0; } else if(((int)a_client->callback) < ((int)b_client->callback)) { return -1; } return 1; } char * cib_pluralSection(const char *a_section) { char *a_section_parent = NULL; if (a_section == NULL) { a_section_parent = crm_strdup("all"); } else if(strcmp(a_section, XML_TAG_CIB) == 0) { a_section_parent = crm_strdup("all"); } else if(strcmp(a_section, XML_CIB_TAG_NODE) == 0) { a_section_parent = crm_strdup(XML_CIB_TAG_NODES); } else if(strcmp(a_section, XML_CIB_TAG_STATE) == 0) { a_section_parent = crm_strdup(XML_CIB_TAG_STATUS); } else if(strcmp(a_section, XML_CIB_TAG_CONSTRAINT) == 0) { a_section_parent = crm_strdup(XML_CIB_TAG_CONSTRAINTS); } else if(strcmp(a_section, "rsc_location") == 0) { a_section_parent = crm_strdup(XML_CIB_TAG_CONSTRAINTS); } else if(strcmp(a_section, "rsc_dependancy") == 0) { a_section_parent = crm_strdup(XML_CIB_TAG_CONSTRAINTS); } else if(strcmp(a_section, "rsc_order") == 0) { a_section_parent = crm_strdup(XML_CIB_TAG_CONSTRAINTS); } else if(strcmp(a_section, XML_CIB_TAG_RESOURCE) == 0) { a_section_parent = crm_strdup(XML_CIB_TAG_RESOURCES); } else if(strcmp(a_section, XML_CIB_TAG_NVPAIR) == 0) { a_section_parent = crm_strdup(XML_CIB_TAG_CRMCONFIG); } else { crm_err("Unknown section %s", a_section); a_section_parent = crm_strdup("all"); } crm_verbose("Plural of %s is %s", crm_str(a_section), a_section_parent); return a_section_parent; } const char * cib_error2string(enum cib_errors return_code) { const char *error_msg = NULL; switch(return_code) { case cib_msg_field_add: error_msg = "failed adding field to cib message"; break; case cib_operation: error_msg = "invalid operation"; break; case cib_create_msg: error_msg = "couldnt create cib message"; break; case cib_client_gone: error_msg = "client left before we could send reply"; break; case cib_not_connected: error_msg = "not connected"; break; case cib_not_authorized: error_msg = "not authorized"; break; case cib_send_failed: error_msg = "send failed"; break; case cib_reply_failed: error_msg = "reply failed"; break; case cib_return_code: error_msg = "no return code"; break; case cib_output_ptr: error_msg = "nowhere to store output"; break; case cib_output_data: error_msg = "corrupt output data"; break; case cib_connection: error_msg = "connection failed"; break; case cib_callback_register: error_msg = "couldnt register callback channel"; break; case cib_authentication: error_msg = ""; break; case cib_registration_msg: error_msg = "invalid registration msg"; break; case cib_callback_token: error_msg = "callback token not found"; break; case cib_missing: error_msg = "cib object missing"; break; case cib_variant: error_msg = "unknown/corrupt cib variant"; break; case CIBRES_MISSING_ID: error_msg = "The id field is missing"; break; case CIBRES_MISSING_TYPE: error_msg = "The type field is missing"; break; case CIBRES_MISSING_FIELD: error_msg = "A required field is missing"; break; case CIBRES_OBJTYPE_MISMATCH: error_msg = "CIBRES_OBJTYPE_MISMATCH"; break; case cib_EXISTS: error_msg = "The object already exists"; break; case cib_NOTEXISTS: error_msg = "The object does not exist"; break; case CIBRES_CORRUPT: error_msg = "The CIB is corrupt"; break; case cib_NOOBJECT: error_msg = "The update was empty"; break; case cib_NOPARENT: error_msg = "The parent object does not exist"; break; case cib_NODECOPY: error_msg = "Failed while copying update"; break; case CIBRES_OTHER: error_msg = "CIBRES_OTHER"; break; case cib_ok: error_msg = "ok"; break; case cib_unknown: error_msg = "Unknown error"; break; case cib_STALE: error_msg = "Discarded old update"; break; case cib_ACTIVATION: error_msg = "Activation Failed"; break; case cib_NOSECTION: error_msg = "Required section was missing"; break; case cib_NOTSUPPORTED: error_msg = "Supplied information is not supported"; break; case cib_not_master: error_msg = "Local service is not the master instance"; break; case cib_client_corrupt: error_msg = "Service client not valid"; break; + case cib_master_timeout: + error_msg = "No master service is currently active"; + break; } if(error_msg == NULL) { crm_err("Unknown CIB Error Code: %d", return_code); error_msg = ""; } return error_msg; } const char * cib_op2string(enum cib_op operation) { const char *operation_msg = NULL; switch(operation) { case 0: operation_msg = "none"; break; case 1: operation_msg = "add"; break; case 2: operation_msg = "modify"; break; case 3: operation_msg = "delete"; break; case CIB_OP_MAX: operation_msg = "invalid operation"; break; } if(operation_msg == NULL) { crm_err("Unknown CIB operation %d", operation); operation_msg = ""; } return operation_msg; } int cib_section2enum(const char *a_section) { if(a_section == NULL || strcmp(a_section, "all") == 0) { return cib_section_all; } else if(strcmp(a_section, XML_CIB_TAG_NODES) == 0) { return cib_section_nodes; } else if(strcmp(a_section, XML_CIB_TAG_STATUS) == 0) { return cib_section_status; } else if(strcmp(a_section, XML_CIB_TAG_CONSTRAINTS) == 0) { return cib_section_constraints; } else if(strcmp(a_section, XML_CIB_TAG_RESOURCES) == 0) { return cib_section_resources; } else if(strcmp(a_section, XML_CIB_TAG_CRMCONFIG) == 0) { return cib_section_crmconfig; } crm_err("Unknown CIB section: %s", a_section); return cib_section_none; } int cib_compare_generation(xmlNodePtr left, xmlNodePtr right) { int int_gen_l = -1; int int_gen_r = -1; const char *gen_l = xmlGetProp(left, XML_ATTR_GENERATION); const char *gen_r = xmlGetProp(right, XML_ATTR_GENERATION); if(gen_l != NULL) int_gen_l = atoi(gen_l); if(gen_r != NULL) int_gen_r = atoi(gen_r); crm_xml_trace(left, "left"); crm_xml_trace(left, "right"); if(int_gen_l < int_gen_r) { crm_trace("lt"); return -1; } else if(int_gen_l > int_gen_r) { crm_trace("gt"); return 1; } crm_trace("eq"); return 0; } xmlNodePtr get_cib_copy(cib_t *cib) { xmlNodePtr xml_cib; int options = cib_scope_local|cib_sync_call; if(cib->cmds->query(cib, NULL, &xml_cib, options) != cib_ok) { crm_err("Couldnt retrieve the CIB"); return NULL; } return xml_cib->children; } xmlNodePtr cib_get_generation(cib_t *cib) { xmlNodePtr the_cib = get_cib_copy(cib); xmlNodePtr generation = create_xml_node(NULL, "generation_tuple"); copy_in_properties(generation, the_cib); free_xml(the_cib); return generation; } /* * The caller should never free the return value */ xmlNodePtr get_object_root(const char *object_type, xmlNodePtr the_root) { const char *node_stack[2]; xmlNodePtr tmp_node = NULL; if(the_root == NULL) { crm_err("CIB root object was NULL"); return NULL; } else if(object_type == NULL) { crm_debug("Returning the whole CIB"); return the_root; } node_stack[0] = XML_CIB_TAG_CONFIGURATION; node_stack[1] = object_type; if(object_type == NULL || strlen(object_type) == 0 || safe_str_eq("all", object_type)) { return the_root; /* get the whole cib */ } else if(strcmp(object_type, XML_CIB_TAG_STATUS) == 0) { /* these live in a different place */ tmp_node = find_xml_node(the_root, XML_CIB_TAG_STATUS); node_stack[0] = XML_CIB_TAG_STATUS; node_stack[1] = NULL; /* } else if(strcmp(object_type, XML_CIB_TAG_CRMCONFIG) == 0) { */ /* /\* these live in a different place too *\/ */ /* tmp_node = find_xml_node(the_root, XML_CIB_TAG_CRMCONFIG); */ /* node_stack[0] = XML_CIB_TAG_CRMCONFIG; */ /* node_stack[1] = NULL; */ } else { tmp_node = find_xml_node_nested(the_root, node_stack, 2); } if (tmp_node == NULL) { crm_err("[cib] Section %s [%s [%s]] not present", the_root->name, node_stack[0], node_stack[1]?node_stack[1]:""); } return tmp_node; } xmlNodePtr create_cib_fragment_adv( xmlNodePtr update, const char *section, const char *source) { gboolean whole_cib = FALSE; xmlNodePtr fragment = create_xml_node(NULL, XML_TAG_FRAGMENT); xmlNodePtr cib = NULL; xmlNodePtr object_root = NULL; char *auto_section = cib_pluralSection(update?update->name:NULL); if(update == NULL) { crm_err("No update to create a fragment for"); crm_free(auto_section); return NULL; } else if(section == NULL) { section = auto_section; } else if(strcmp(auto_section, section) != 0) { crm_err("Values for update (tag=%s) and section (%s)" " were not consistent", update->name, section); crm_free(auto_section); return NULL; } if(strcmp(section, "all")==0 && strcmp(update->name, XML_TAG_CIB)==0) { whole_cib = TRUE; } set_xml_property_copy(fragment, XML_ATTR_SECTION, section); if(whole_cib == FALSE) { cib = createEmptyCib(); object_root = get_object_root(section, cib); xmlAddChildList(object_root, xmlCopyNodeList(update)); } else { cib = xmlCopyNodeList(update); } xmlAddChild(fragment, cib); set_xml_property_copy(cib, "debug_source", source); crm_free(auto_section); crm_debug("Verifying created fragment"); if(verifyCibXml(cib) == FALSE) { crm_err("Fragment creation failed"); crm_err("[src] %s", dump_xml_formatted(update)); crm_err("[created] %s", dump_xml_formatted(fragment)); free_xml(fragment); fragment = NULL; } return fragment; } /* * It is the callers responsibility to free both the new CIB (output) * and the new CIB (input) */ xmlNodePtr createEmptyCib(void) { xmlNodePtr cib_root = NULL, config = NULL, status = NULL; cib_root = create_xml_node(NULL, XML_TAG_CIB); config = create_xml_node(cib_root, XML_CIB_TAG_CONFIGURATION); status = create_xml_node(cib_root, XML_CIB_TAG_STATUS); set_node_tstamp(cib_root); set_node_tstamp(config); set_node_tstamp(status); set_xml_property_copy(cib_root, "version", "1"); set_xml_property_copy(cib_root, "generated", XML_BOOLEAN_TRUE); create_xml_node(config, XML_CIB_TAG_CRMCONFIG); create_xml_node(config, XML_CIB_TAG_NODES); create_xml_node(config, XML_CIB_TAG_RESOURCES); create_xml_node(config, XML_CIB_TAG_CONSTRAINTS); if (verifyCibXml(cib_root)) { return cib_root; } crm_crit("The generated CIB did not pass integrity testing!!" " All hope is lost."); return NULL; } gboolean verifyCibXml(xmlNodePtr cib) { gboolean is_valid = TRUE; xmlNodePtr tmp_node = NULL; if (cib == NULL) { crm_warn("XML Buffer was empty."); return FALSE; } tmp_node = get_object_root(XML_CIB_TAG_NODES, cib); if (tmp_node == NULL) is_valid = FALSE; tmp_node = get_object_root(XML_CIB_TAG_RESOURCES, cib); if (tmp_node == NULL) is_valid = FALSE; tmp_node = get_object_root(XML_CIB_TAG_CONSTRAINTS, cib); if (tmp_node == NULL) is_valid = FALSE; tmp_node = get_object_root(XML_CIB_TAG_STATUS, cib); if (tmp_node == NULL) is_valid = FALSE; tmp_node = get_object_root(XML_CIB_TAG_CRMCONFIG, cib); if (tmp_node == NULL) is_valid = FALSE; /* more integrity tests */ return is_valid; } gboolean verify_cib_cmds(cib_t *cib) { gboolean valid = TRUE; if(cib->cmds->variant_op == NULL) { crm_err("Operation variant_op not set"); valid = FALSE; } if(cib->cmds->signon == NULL) { crm_err("Operation signon not set"); valid = FALSE; } if(cib->cmds->signoff == NULL) { crm_err("Operation signoff not set"); valid = FALSE; } if(cib->cmds->free == NULL) { crm_err("Operation free not set"); valid = FALSE; } if(cib->cmds->set_op_callback == NULL) { crm_err("Operation set_op_callback not set"); valid = FALSE; } if(cib->cmds->add_notify_callback == NULL) { crm_err("Operation add_notify_callback not set"); valid = FALSE; } if(cib->cmds->del_notify_callback == NULL) { crm_err("Operation del_notify_callback not set"); valid = FALSE; } if(cib->cmds->set_connection_dnotify == NULL) { crm_err("Operation set_connection_dnotify not set"); valid = FALSE; } if(cib->cmds->channel == NULL) { crm_err("Operation channel not set"); valid = FALSE; } if(cib->cmds->inputfd == NULL) { crm_err("Operation inputfd not set"); valid = FALSE; } if(cib->cmds->noop == NULL) { crm_err("Operation noop not set"); valid = FALSE; } if(cib->cmds->ping == NULL) { crm_err("Operation ping not set"); valid = FALSE; } if(cib->cmds->query == NULL) { crm_err("Operation query not set"); valid = FALSE; } if(cib->cmds->query_from == NULL) { crm_err("Operation query_from not set"); valid = FALSE; } if(cib->cmds->is_master == NULL) { crm_err("Operation is_master not set"); valid = FALSE; } if(cib->cmds->set_master == NULL) { crm_err("Operation set_master not set"); valid = FALSE; } if(cib->cmds->set_slave == NULL) { crm_err("Operation set_slave not set"); valid = FALSE; } if(cib->cmds->set_slave_all == NULL) { crm_err("Operation set_slave_all not set"); valid = FALSE; } if(cib->cmds->sync == NULL) { crm_err("Operation sync not set"); valid = FALSE; } if(cib->cmds->sync_from == NULL) { crm_err("Operation sync_from not set"); valid = FALSE; } if(cib->cmds->bump_epoch == NULL) { crm_err("Operation bump_epoch not set"); valid = FALSE; } if(cib->cmds->create == NULL) { crm_err("Operation create not set"); valid = FALSE; } if(cib->cmds->modify == NULL) { crm_err("Operation modify not set"); valid = FALSE; } if(cib->cmds->replace == NULL) { crm_err("Operation replace not set"); valid = FALSE; } if(cib->cmds->delete == NULL) { crm_err("Operation delete not set"); valid = FALSE; } if(cib->cmds->erase == NULL) { crm_err("Operation erase not set"); valid = FALSE; } if(cib->cmds->quit == NULL) { crm_err("Operation quit not set"); valid = FALSE; } if(cib->cmds->msgready == NULL) { crm_err("Operation msgready not set"); valid = FALSE; } if(cib->cmds->rcvmsg == NULL) { crm_err("Operation rcvmsg not set"); valid = FALSE; } if(cib->cmds->dispatch == NULL) { crm_err("Operation dispatch not set"); valid = FALSE; } return valid; }