Page MenuHomeClusterLabs Projects

No OneTemporary

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 <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <sys/param.h>
#include <crm/crm.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <hb_api.h>
#include <clplumbing/uids.h>
#include <clplumbing/Gmain_timeout.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/ctrl.h>
#include <crm/common/ipc.h>
#include <crm/cib.h>
#include <getopt.h>
#include <ha_msg.h> /* someone complaining about _ha_msg_mod not being found */
#include <crm/dmalloc_wrapper.h>
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) <id>\tid of the object being operated on\n",
XML_ATTR_ID, 'i');
fprintf(stream, "\t--%s (-%c) <type>\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) <string>\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 <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <sys/param.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <hb_api.h>
#include <clplumbing/uids.h>
#include <crm/crm.h>
#include <crm/cib.h>
#include <crm/msg_xml.h>
#include <crm/common/ipc.h>
#include <crm/common/ctrl.h>
#include <crm/common/xml.h>
#include <crm/common/msg.h>
#include <cibio.h>
#include <callbacks.h>
#include <cibmessages.h>
#include <crm/dmalloc_wrapper.h>
+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 <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <hb_api.h>
#include <clplumbing/ipc.h>
#include <clplumbing/GSource.h>
#include <crm/crm.h>
#include <crm/cib.h>
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 <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <sys/param.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <hb_api.h>
#include <clplumbing/uids.h>
#include <clplumbing/coredumps.h>
+#include <clplumbing/Gmain_timeout.h>
/* #include <ocf/oc_event.h> */
#include <crm/crm.h>
#include <crm/cib.h>
#include <crm/msg_xml.h>
#include <crm/common/ipc.h>
#include <crm/common/ctrl.h>
#include <crm/common/xml.h>
#include <crm/common/msg.h>
#include <cibio.h>
#include <callbacks.h>
#include <crm/dmalloc_wrapper.h>
/* #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 <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <sys/param.h>
#include <crm/crm.h>
#include <string.h>
#include <crmd_fsa.h>
#include <libxml/tree.h>
#include <heartbeat.h>
#include <hb_api.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/msg.h>
#include <crm/cib.h>
#include <crmd.h>
#include <crmd_messages.h>
#include <crmd_callbacks.h>
#include <crm/dmalloc_wrapper.h>
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 <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <sys/param.h>
#include <crm/crm.h>
#include <crmd_fsa.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h> /* for access */
#include <clplumbing/cl_signal.h>
#include <errno.h>
#include <crm/cib.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crmd.h>
#include <crmd_messages.h>
#include <crmd_callbacks.h>
#include <lrm/raexec.h>
#include <crm/dmalloc_wrapper.h>
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 "<unknown>";
}
/* 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)
{
/*
<status>
<nodes_status id=uname>
<lrm>
<lrm_resources>
<lrm_resource id=>
</...>
*/
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 <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <sys/param.h>
#include <crm/crm.h>
#include <crm/cib.h>
#include <crm/msg_xml.h>
#include <crm/common/msg.h>
#include <crm/common/xml.h>
#include <tengine.h>
#include <heartbeat.h>
#include <clplumbing/Gmain_timeout.h>
#include <lrm/lrm_api.h>
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_msg op=XML_LRM_ATTR_TASK to=XML_RES_ATTR_TARGET>
*/
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
/*
<msg_data>
<rsc_op id="operation number" on_node="" task="">
<resource>...</resource>
*/
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 <andrew@beekhof.net>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# 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="'<rsc_location id=\"${uuid1}\" rsc=\"${rsc}\">
<rule id=\"${uuid2}\" result=\"can\"/>
<rule id=\"${uuid3}\" score=\"1000\" boolean_op=\"or\">
<expression attribute=\"uname\" operation=\"eq\" value=\"${test_node_1}\"/>
</rule>
</rsc_location>'"
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="'<rsc_location id=\"${uuid1}\" rsc=\"${rsc}\">
<rule id=\"${uuid2}\" result=\"can\"/>
<rule id=\"${uuid3}\" score=\"1000\" boolean_op=\"or\">
<expression attribute=\"uname\" operation=\"eq\" value=\"${test_node_2}\"/>
</rule>
</rsc_location>'"
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="<nvpair name=\"1\" value=\"${ip_rsc_1}\"/>"
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="<nvpair name=\"1\" value=\"${ip_rsc_2}\"/>"
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 <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef CIB__H
#define CIB__H
#include <libxml/tree.h>
#include <clplumbing/ipc.h>
#include <ha_msg.h>
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 <portability.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <glib.h>
#include <heartbeat.h>
#include <clplumbing/ipc.h>
#include <crm/crm.h>
#include <crm/cib.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
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, &current_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 = "<unknown error>";
}
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 = "<unknown operation>";
}
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;
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Jul 10, 2:25 AM (1 d, 15 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2009733
Default Alt Text
(152 KB)

Event Timeline