Page MenuHomeClusterLabs Projects

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/crmd/Makefile.am b/crmd/Makefile.am
index 3742582756..2db2410ff0 100644
--- a/crmd/Makefile.am
+++ b/crmd/Makefile.am
@@ -1,76 +1,76 @@
#
# Copyright (C) 2004 Andrew Beekhof
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
MAINTAINERCLEANFILES = Makefile.in
INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \
-I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl
halibdir = $(CRM_DAEMON_DIR)
## binary progs
halib_PROGRAMS = crmd
## SOURCES
noinst_HEADERS = crmd.h crmd_fsa.h crmd_messages.h fsa_defines.h \
fsa_matrix.h fsa_proto.h crmd_utils.h crmd_callbacks.h \
crmd_lrm.h te_callbacks.h tengine.h
crmd_SOURCES = main.c crmd.c corosync.c \
fsa.c control.c messages.c membership.c callbacks.c \
election.c join_client.c join_dc.c subsystems.c \
- cib.c pengine.c tengine.c lrm.c \
+ cib.c pengine.c tengine.c lrm.c lrm_state.c \
utils.c misc.c te_events.c te_actions.c te_utils.c te_callbacks.c
if BUILD_HEARTBEAT_SUPPORT
crmd_SOURCES += heartbeat.c
endif
crmd_LDADD = $(top_builddir)/lib/fencing/libstonithd.la \
$(top_builddir)/lib/transition/libtransitioner.la \
$(top_builddir)/lib/pengine/libpe_rules.la \
$(top_builddir)/lib/cib/libcib.la \
$(top_builddir)/lib/cluster/libcrmcluster.la \
$(top_builddir)/lib/common/libcrmcommon.la \
$(top_builddir)/lib/services/libcrmservice.la \
$(top_builddir)/lib/lrmd/liblrmd.la \
$(CLUSTERLIBS)
if BUILD_XML_HELP
man7_MANS = crmd.7
%.xml: %
$(top_builddir)/crmd/$< metadata | $(XSLTPROC) --nonet --novalid --stringparam man.name $< $(top_srcdir)/xml/ocf-meta2man.xsl - > $(top_builddir)/crmd/$@
%.7: %.xml
$(XSLTPROC) $(MANPAGE_XSLT) $(top_builddir)/crmd/$<
endif
clean-generic:
rm -f *.log *.debug *.xml *~
install-exec-local:
uninstall-local:
graphs: fsa_inputs.png fsa_inputs_by_action.png fsa_actions_by_state.png
%.png: %.dot
dot -Tpng $< > $@
%.dot : fsa_matrix.h make_dot.pl
perl $(top_srcdir)/crmd/make_dot.pl $(top_srcdir)/crmd/fsa_matrix.h $(top_builddir)/crmd
diff --git a/crmd/callbacks.c b/crmd/callbacks.c
index 86c71c79f0..5248f0ea82 100644
--- a/crmd/callbacks.c
+++ b/crmd/callbacks.c
@@ -1,255 +1,246 @@
/*
* 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 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <crm_internal.h>
#include <sys/param.h>
#include <crm/crm.h>
#include <string.h>
#include <crmd_fsa.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/cluster.h>
#include <crm/cib.h>
#include <crmd.h>
#include <crmd_messages.h>
#include <crmd_callbacks.h>
#include <crmd_lrm.h>
#include <tengine.h>
#include <membership.h>
void crmd_ha_connection_destroy(gpointer user_data);
/* From join_dc... */
extern gboolean check_join_state(enum crmd_fsa_state cur_state, const char *source);
void
crmd_ha_connection_destroy(gpointer user_data)
{
crm_trace("Invoked");
if (is_set(fsa_input_register, R_HA_DISCONNECTED)) {
/* we signed out, so this is expected */
crm_info("Heartbeat disconnection complete");
return;
}
crm_crit("Lost connection to heartbeat service!");
register_fsa_input(C_HA_DISCONNECT, I_ERROR, NULL);
trigger_fsa(fsa_source);
}
void
crmd_ha_msg_filter(xmlNode * msg)
{
if (AM_I_DC) {
const char *sys_from = crm_element_value(msg, F_CRM_SYS_FROM);
if (safe_str_eq(sys_from, CRM_SYSTEM_DC)) {
const char *from = crm_element_value(msg, F_ORIG);
if (safe_str_neq(from, fsa_our_uname)) {
int level = LOG_INFO;
const char *op = crm_element_value(msg, F_CRM_TASK);
/* make sure the election happens NOW */
if (fsa_state != S_ELECTION) {
ha_msg_input_t new_input;
level = LOG_WARNING;
new_input.msg = msg;
register_fsa_error_adv(C_FSA_INTERNAL, I_ELECTION, NULL, &new_input,
__FUNCTION__);
}
do_crm_log(level, "Another DC detected: %s (op=%s)", from, op);
goto done;
}
}
} else {
const char *sys_to = crm_element_value(msg, F_CRM_SYS_TO);
if (safe_str_eq(sys_to, CRM_SYSTEM_DC)) {
return;
}
}
/* crm_log_xml_trace("HA[inbound]", msg); */
route_message(C_HA_MESSAGE, msg);
done:
trigger_fsa(fsa_source);
}
-extern gboolean process_lrm_event(lrmd_event_data_t * op);
-
-void
-lrm_op_callback(lrmd_event_data_t * op)
-{
- CRM_CHECK(op != NULL, return);
- process_lrm_event(op);
-}
-
void
peer_update_callback(enum crm_status_type type, crm_node_t * node, const void *data)
{
uint32_t old = 0;
const char *status = NULL;
set_bit(fsa_input_register, R_PEER_DATA);
if (node->uname == NULL) {
return;
}
switch (type) {
case crm_status_uname:
/* If we've never seen the node, then it also wont be in the status section */
crm_info("%s is now %s", node->uname, node->state);
return;
case crm_status_nstate:
crm_info("%s is now %s (was %s)", node->uname, node->state, (const char *)data);
if(safe_str_neq(data, node->state)) {
/* State did not change */
return;
}
break;
case crm_status_processes:
if (data) {
old = *(const uint32_t *)data;
}
/* crmd_proc_update(node, proc_flags); */
status = (node->processes & proc_flags) ? ONLINESTATUS : OFFLINESTATUS;
crm_info("Client %s/%s now has status [%s] (DC=%s)",
node->uname, peer2text(proc_flags), status, AM_I_DC ? "true" : crm_str(fsa_our_dc));
if (((node->processes ^ old) & proc_flags) == 0) {
/* Peer process did not change */
crm_trace("No change %6x %6x %6x", old, node->processes, proc_flags);
return;
} else if (is_set(fsa_input_register, R_CIB_CONNECTED) == FALSE) {
crm_trace("Not connected");
return;
} else if (fsa_state == S_STOPPING) {
crm_trace("Stopping");
return;
}
if (safe_str_eq(node->uname, fsa_our_dc) && crm_is_peer_active(node) == FALSE) {
/* Did the DC leave us? */
crm_notice("Got client status callback - our DC is dead");
register_fsa_input(C_CRMD_STATUS_CALLBACK, I_ELECTION, NULL);
}
break;
}
if (AM_I_DC) {
xmlNode *update = NULL;
crm_action_t *down = match_down_event(0, node->uuid, NULL);
gboolean alive = crm_is_peer_active(node);
if(alive && type == crm_status_processes) {
register_fsa_input_before(C_FSA_INTERNAL, I_NODE_JOIN, NULL);
}
crm_trace("Alive=%d, down=%p", alive, down);
if (down) {
const char *task = crm_element_value(down->xml, XML_LRM_ATTR_TASK);
if (alive && safe_str_eq(task, CRM_OP_FENCE)) {
crm_info("Node return implies stonith of %s (action %d) completed", node->uname, down->id);
erase_status_tag(node->uname, XML_CIB_TAG_LRM, cib_scope_local);
erase_status_tag(node->uname, XML_TAG_TRANSIENT_NODEATTRS, cib_scope_local);
/* down->confirmed = TRUE; Only stonith-ng returning should imply completion */
down->sent_update = TRUE; /* Prevent tengine_stonith_callback() from calling send_stonith_update() */
} else if (safe_str_eq(task, CRM_OP_FENCE)) {
crm_trace("Waiting for stonithd to report the fencing of %s is complete", node->uname); /* via tengine_stonith_callback() */
} else if(alive == FALSE) {
crm_notice("%s of %s (op %d) is complete", task, node->uname, down->id);
/* down->confirmed = TRUE; Only stonith-ng returning should imply completion */
stop_te_timer(down->timer);
erase_node_from_join(node->uname);
crm_update_peer_expected(__FUNCTION__, node, CRMD_JOINSTATE_DOWN);
check_join_state(fsa_state, __FUNCTION__);
update_graph(transition_graph, down);
trigger_graph();
} else {
crm_trace("Other %p", down);
}
} else if(alive == FALSE) {
crm_notice("Stonith/shutdown of %s not matched", node->uname);
erase_node_from_join(node->uname);
crm_update_peer_expected(__FUNCTION__, node, CRMD_JOINSTATE_DOWN);
check_join_state(fsa_state, __FUNCTION__);
abort_transition(INFINITY, tg_restart, "Node failure", NULL);
fail_incompletable_actions(transition_graph, node->uuid);
} else {
crm_trace("Other %p", down);
}
update = do_update_node_cib(node, node_update_peer, NULL, __FUNCTION__);
fsa_cib_anon_update(
XML_CIB_TAG_STATUS, update, cib_scope_local | cib_quorum_override | cib_can_create);
free_xml(update);
}
trigger_fsa(fsa_source);
}
void
crmd_cib_connection_destroy(gpointer user_data)
{
CRM_CHECK(user_data == fsa_cib_conn,;);
crm_trace("Invoked");
trigger_fsa(fsa_source);
fsa_cib_conn->state = cib_disconnected;
if (is_set(fsa_input_register, R_CIB_CONNECTED) == FALSE) {
crm_info("Connection to the CIB terminated...");
return;
}
/* eventually this will trigger a reconnect, not a shutdown */
crm_err("Connection to the CIB terminated...");
register_fsa_input(C_FSA_INTERNAL, I_ERROR, NULL);
clear_bit(fsa_input_register, R_CIB_CONNECTED);
return;
}
gboolean
crm_fsa_trigger(gpointer user_data)
{
crm_trace("Invoked (queue len: %d)", g_list_length(fsa_message_queue));
s_crmd_fsa(C_FSA_INTERNAL);
crm_trace("Exited (queue len: %d)", g_list_length(fsa_message_queue));
return TRUE;
}
diff --git a/crmd/control.c b/crmd/control.c
index 24f76dc885..6312e53c53 100644
--- a/crmd/control.c
+++ b/crmd/control.c
@@ -1,890 +1,885 @@
/*
* 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 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <crm_internal.h>
#include <sys/param.h>
#include <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/pengine/rules.h>
#include <crm/cluster/internal.h>
#include <crm/common/ipcs.h>
#include <crmd.h>
#include <crmd_fsa.h>
#include <fsa_proto.h>
#include <crmd_messages.h>
#include <crmd_callbacks.h>
#include <crmd_lrm.h>
#include <tengine.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <grp.h>
qb_ipcs_service_t *ipcs = NULL;
extern gboolean crm_connect_corosync(crm_cluster_t *cluster);
extern void crmd_ha_connection_destroy(gpointer user_data);
void crm_shutdown(int nsig);
gboolean crm_read_options(gpointer user_data);
gboolean fsa_has_quorum = FALSE;
crm_trigger_t *fsa_source = NULL;
crm_trigger_t *config_read = NULL;
/* A_HA_CONNECT */
void
do_ha_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)
{
gboolean registered = FALSE;
static crm_cluster_t *cluster = NULL;
if(cluster == NULL) {
cluster = calloc(1, sizeof(crm_cluster_t));
}
if (action & A_HA_DISCONNECT) {
crm_cluster_disconnect(cluster);
crm_info("Disconnected from the cluster");
#if SUPPORT_HEARTBEAT
set_bit(fsa_input_register, R_HA_DISCONNECTED);
#endif
}
if (action & A_HA_CONNECT) {
crm_set_status_callback(&peer_update_callback);
if (is_openais_cluster()) {
#if SUPPORT_COROSYNC
registered = crm_connect_corosync(cluster);
#endif
} else if (is_heartbeat_cluster()) {
#if SUPPORT_HEARTBEAT
cluster->destroy = crmd_ha_connection_destroy;
cluster->hb_dispatch = crmd_ha_msg_callback;
registered = crm_cluster_connect(cluster);
fsa_cluster_conn = cluster->hb_conn;
crm_trace("Be informed of Node Status changes");
if (registered &&
fsa_cluster_conn->llc_ops->set_nstatus_callback(fsa_cluster_conn,
crmd_ha_status_callback,
fsa_cluster_conn) != HA_OK) {
crm_err("Cannot set nstatus callback: %s",
fsa_cluster_conn->llc_ops->errmsg(fsa_cluster_conn));
registered = FALSE;
}
crm_trace("Be informed of CRM Client Status changes");
if (registered &&
fsa_cluster_conn->llc_ops->set_cstatus_callback(fsa_cluster_conn,
crmd_client_status_callback,
fsa_cluster_conn) != HA_OK) {
crm_err("Cannot set cstatus callback: %s",
fsa_cluster_conn->llc_ops->errmsg(fsa_cluster_conn));
registered = FALSE;
}
if (registered) {
crm_trace("Requesting an initial dump of CRMD client_status");
fsa_cluster_conn->llc_ops->client_status(fsa_cluster_conn, NULL, CRM_SYSTEM_CRMD,
-1);
}
#endif
}
fsa_our_uname = cluster->uname;
fsa_our_uuid = cluster->uuid;
if (registered == FALSE) {
set_bit(fsa_input_register, R_HA_DISCONNECTED);
register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
return;
}
populate_cib_nodes(node_update_none, __FUNCTION__);
clear_bit(fsa_input_register, R_HA_DISCONNECTED);
crm_info("Connected to the cluster");
}
if (action & ~(A_HA_CONNECT | A_HA_DISCONNECT)) {
crm_err("Unexpected action %s in %s", fsa_action2string(action), __FUNCTION__);
}
}
/* A_SHUTDOWN */
void
do_shutdown(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)
{
/* just in case */
set_bit(fsa_input_register, R_SHUTDOWN);
if (is_heartbeat_cluster()) {
if (is_set(fsa_input_register, pe_subsystem->flag_connected)) {
crm_info("Terminating the %s", pe_subsystem->name);
if (stop_subsystem(pe_subsystem, TRUE) == FALSE) {
/* its gone... */
crm_err("Faking %s exit", pe_subsystem->name);
clear_bit(fsa_input_register, pe_subsystem->flag_connected);
} else {
crm_info("Waiting for subsystems to exit");
crmd_fsa_stall(FALSE);
}
}
crm_info("All subsystems stopped, continuing");
}
if (stonith_api) {
/* Prevent it from comming up again */
clear_bit(fsa_input_register, R_ST_REQUIRED);
crm_info("Disconnecting STONITH...");
stonith_api->cmds->disconnect(stonith_api);
}
}
/* A_SHUTDOWN_REQ */
void
do_shutdown_req(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)
{
xmlNode *msg = NULL;
crm_info("Sending shutdown request to %s", crm_str(fsa_our_dc));
msg = create_request(CRM_OP_SHUTDOWN_REQ, NULL, NULL, CRM_SYSTEM_DC, CRM_SYSTEM_CRMD, NULL);
/* set_bit(fsa_input_register, R_STAYDOWN); */
if (send_cluster_message(NULL, crm_msg_crmd, msg, TRUE) == FALSE) {
register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
}
free_xml(msg);
}
extern crm_ipc_t *attrd_ipc;
extern char *max_generation_from;
extern xmlNode *max_generation_xml;
extern GHashTable *resource_history;
extern GHashTable *voted;
extern GHashTable *reload_hash;
void log_connected_client(gpointer key, gpointer value, gpointer user_data);
void
log_connected_client(gpointer key, gpointer value, gpointer user_data)
{
crm_client_t *client = value;
crm_err("%s is still connected at exit", crm_client_name(client));
}
int
crmd_exit(int rc)
{
GListPtr gIter = NULL;
if(attrd_ipc) {
crm_ipc_close(attrd_ipc);
crm_ipc_destroy(attrd_ipc);
}
if(crmd_mainloop) {
g_main_loop_quit(crmd_mainloop);
g_main_loop_unref(crmd_mainloop);
}
#if SUPPORT_HEARTBEAT
if (fsa_cluster_conn) {
fsa_cluster_conn->llc_ops->delete(fsa_cluster_conn);
fsa_cluster_conn = NULL;
}
#endif
for(gIter = fsa_message_queue; gIter != NULL; gIter = gIter->next) {
fsa_data_t *fsa_data = gIter->data;
crm_info("Dropping %s: [ state=%s cause=%s origin=%s ]",
fsa_input2string(fsa_data->fsa_input),
fsa_state2string(fsa_state),
fsa_cause2string(fsa_data->fsa_cause), fsa_data->origin);
delete_fsa_input(fsa_data);
}
g_list_free(fsa_message_queue);
fsa_message_queue = NULL;
crm_client_cleanup();
empty_uuid_cache();
crm_peer_destroy();
clear_bit(fsa_input_register, R_MEMBERSHIP);
if (te_subsystem->client && te_subsystem->client->ipcs) {
crm_debug("Full destroy: TE");
qb_ipcs_disconnect(te_subsystem->client->ipcs);
}
free(te_subsystem);
if (pe_subsystem->client && pe_subsystem->client->ipcs) {
crm_debug("Full destroy: PE");
qb_ipcs_disconnect(pe_subsystem->client->ipcs);
}
free(pe_subsystem);
free(cib_subsystem);
if (integrated_nodes) {
g_hash_table_destroy(integrated_nodes);
}
if (finalized_nodes) {
g_hash_table_destroy(finalized_nodes);
}
if (confirmed_nodes) {
g_hash_table_destroy(confirmed_nodes);
}
if (reload_hash) {
g_hash_table_destroy(reload_hash);
}
- if (resource_history) {
- g_hash_table_destroy(resource_history);
- }
if (voted) {
g_hash_table_destroy(voted);
}
cib_delete(fsa_cib_conn);
fsa_cib_conn = NULL;
- if (fsa_lrm_conn) {
- lrmd_api_delete(fsa_lrm_conn);
- fsa_lrm_conn = NULL;
- }
+ lrm_state_destroy_all();
free(transition_timer);
free(integration_timer);
free(finalization_timer);
free(election_trigger);
free(election_timeout);
free(shutdown_escalation_timer);
free(wait_timer);
free(recheck_timer);
free(fsa_our_dc_version);
free(fsa_our_uname);
free(fsa_our_uuid);
free(fsa_our_dc);
free(max_generation_from);
free_xml(max_generation_xml);
return crm_exit(rc);
}
/* A_EXIT_0, A_EXIT_1 */
void
do_exit(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)
{
int exit_code = 0;
int log_level = LOG_INFO;
const char *exit_type = "gracefully";
if (action & A_EXIT_1) {
exit_code = 1;
log_level = LOG_ERR;
exit_type = "forcefully";
}
verify_stopped(cur_state, LOG_ERR);
do_crm_log(log_level, "Performing %s - %s exiting the CRMd",
fsa_action2string(action), exit_type);
if (is_set(fsa_input_register, R_IN_RECOVERY)) {
crm_err("Could not recover from internal error");
exit_code = 2;
}
if (is_set(fsa_input_register, R_STAYDOWN)) {
crm_warn("Inhibiting respawn by Heartbeat");
exit_code = 100;
}
crm_info("[%s] stopped (%d)", crm_system_name, exit_code);
delete_fsa_input(msg_data);
crmd_exit(exit_code);
}
/* A_STARTUP */
void
do_startup(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)
{
int was_error = 0;
int interval = 1; /* seconds between DC heartbeats */
crm_debug("Registering Signal Handlers");
mainloop_add_signal(SIGTERM, crm_shutdown);
fsa_source = mainloop_add_trigger(G_PRIORITY_HIGH, crm_fsa_trigger, NULL);
config_read = mainloop_add_trigger(G_PRIORITY_HIGH, crm_read_options, NULL);
crm_debug("Creating CIB and LRM objects");
fsa_cib_conn = cib_new();
- fsa_lrm_conn = lrmd_api_new();
+
+ lrm_state_init_local();
/* set up the timers */
transition_timer = calloc(1, sizeof(fsa_timer_t));
integration_timer = calloc(1, sizeof(fsa_timer_t));
finalization_timer = calloc(1, sizeof(fsa_timer_t));
election_trigger = calloc(1, sizeof(fsa_timer_t));
election_timeout = calloc(1, sizeof(fsa_timer_t));
shutdown_escalation_timer = calloc(1, sizeof(fsa_timer_t));
wait_timer = calloc(1, sizeof(fsa_timer_t));
recheck_timer = calloc(1, sizeof(fsa_timer_t));
interval = interval * 1000;
if (election_trigger != NULL) {
election_trigger->source_id = 0;
election_trigger->period_ms = -1;
election_trigger->fsa_input = I_DC_TIMEOUT;
election_trigger->callback = crm_timer_popped;
election_trigger->repeat = FALSE;
} else {
was_error = TRUE;
}
if (election_timeout != NULL) {
election_timeout->source_id = 0;
election_timeout->period_ms = -1;
election_timeout->fsa_input = I_ELECTION_DC;
election_timeout->callback = crm_timer_popped;
election_timeout->repeat = FALSE;
} else {
was_error = TRUE;
}
if (transition_timer != NULL) {
transition_timer->source_id = 0;
transition_timer->period_ms = -1;
transition_timer->fsa_input = I_PE_CALC;
transition_timer->callback = crm_timer_popped;
transition_timer->repeat = FALSE;
} else {
was_error = TRUE;
}
if (integration_timer != NULL) {
integration_timer->source_id = 0;
integration_timer->period_ms = -1;
integration_timer->fsa_input = I_INTEGRATED;
integration_timer->callback = crm_timer_popped;
integration_timer->repeat = FALSE;
} else {
was_error = TRUE;
}
if (finalization_timer != NULL) {
finalization_timer->source_id = 0;
finalization_timer->period_ms = -1;
finalization_timer->fsa_input = I_FINALIZED;
finalization_timer->callback = crm_timer_popped;
finalization_timer->repeat = FALSE;
/* for possible enabling... a bug in the join protocol left
* a slave in S_PENDING while we think its in S_NOT_DC
*
* raising I_FINALIZED put us into a transition loop which is
* never resolved.
* in this loop we continually send probes which the node
* NACK's because its in S_PENDING
*
* if we have nodes where heartbeat is active but the
* CRM is not... then this will be handled in the
* integration phase
*/
finalization_timer->fsa_input = I_ELECTION;
} else {
was_error = TRUE;
}
if (shutdown_escalation_timer != NULL) {
shutdown_escalation_timer->source_id = 0;
shutdown_escalation_timer->period_ms = -1;
shutdown_escalation_timer->fsa_input = I_STOP;
shutdown_escalation_timer->callback = crm_timer_popped;
shutdown_escalation_timer->repeat = FALSE;
} else {
was_error = TRUE;
}
if (wait_timer != NULL) {
wait_timer->source_id = 0;
wait_timer->period_ms = 2000;
wait_timer->fsa_input = I_NULL;
wait_timer->callback = crm_timer_popped;
wait_timer->repeat = FALSE;
} else {
was_error = TRUE;
}
if (recheck_timer != NULL) {
recheck_timer->source_id = 0;
recheck_timer->period_ms = -1;
recheck_timer->fsa_input = I_PE_CALC;
recheck_timer->callback = crm_timer_popped;
recheck_timer->repeat = FALSE;
} else {
was_error = TRUE;
}
/* set up the sub systems */
cib_subsystem = calloc(1, sizeof(struct crm_subsystem_s));
te_subsystem = calloc(1, sizeof(struct crm_subsystem_s));
pe_subsystem = calloc(1, sizeof(struct crm_subsystem_s));
if (cib_subsystem != NULL) {
cib_subsystem->pid = -1;
cib_subsystem->name = CRM_SYSTEM_CIB;
cib_subsystem->flag_connected = R_CIB_CONNECTED;
cib_subsystem->flag_required = R_CIB_REQUIRED;
} else {
was_error = TRUE;
}
if (te_subsystem != NULL) {
te_subsystem->pid = -1;
te_subsystem->name = CRM_SYSTEM_TENGINE;
te_subsystem->flag_connected = R_TE_CONNECTED;
te_subsystem->flag_required = R_TE_REQUIRED;
} else {
was_error = TRUE;
}
if (pe_subsystem != NULL) {
pe_subsystem->pid = -1;
pe_subsystem->path = CRM_DAEMON_DIR;
pe_subsystem->name = CRM_SYSTEM_PENGINE;
pe_subsystem->command = CRM_DAEMON_DIR "/" CRM_SYSTEM_PENGINE;
pe_subsystem->args = NULL;
pe_subsystem->flag_connected = R_PE_CONNECTED;
pe_subsystem->flag_required = R_PE_REQUIRED;
} else {
was_error = TRUE;
}
if (was_error == FALSE && is_heartbeat_cluster()) {
if(start_subsystem(pe_subsystem) == FALSE) {
was_error = TRUE;
}
}
if (was_error) {
register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
}
welcomed_nodes = g_hash_table_new_full(crm_str_hash, g_str_equal,
g_hash_destroy_str, g_hash_destroy_str);
integrated_nodes = g_hash_table_new_full(crm_str_hash, g_str_equal,
g_hash_destroy_str, g_hash_destroy_str);
finalized_nodes = g_hash_table_new_full(crm_str_hash, g_str_equal,
g_hash_destroy_str, g_hash_destroy_str);
confirmed_nodes = g_hash_table_new_full(crm_str_hash, g_str_equal,
g_hash_destroy_str, g_hash_destroy_str);
}
static int32_t
crmd_ipc_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid)
{
crm_trace("Connection %p", c);
if(crm_client_new(c, uid, gid) == NULL) {
return -EIO;
}
return 0;
}
static void
crmd_ipc_created(qb_ipcs_connection_t *c)
{
crm_trace("Connection %p", c);
}
static int32_t
crmd_ipc_dispatch(qb_ipcs_connection_t *c, void *data, size_t size)
{
uint32_t id = 0;
uint32_t flags = 0;
crm_client_t *client = crm_client_get(c);
xmlNode *msg = crm_ipcs_recv(client, data, size, &id, &flags);
crm_trace("Invoked: %s", crm_client_name(client));
if(flags & crm_ipc_client_response) {
crm_ipcs_send_ack(client, id, "ack", __FUNCTION__, __LINE__);
}
if (msg == NULL) {
return 0;
}
#if ENABLE_ACL
determine_request_user(client->user, msg, F_CRM_USER);
#endif
crm_trace("Processing msg from %s", crm_client_name(client));
crm_log_xml_trace(msg, "CRMd[inbound]");
crm_xml_add(msg, F_CRM_SYS_FROM, client->id);
if (crmd_authorize_message(msg, client)) {
route_message(C_IPC_MESSAGE, msg);
}
trigger_fsa(fsa_source);
free_xml(msg);
return 0;
}
static int32_t
crmd_ipc_closed(qb_ipcs_connection_t *c)
{
crm_client_t *client = crm_client_get(c);
struct crm_subsystem_s *the_subsystem = NULL;
crm_trace("Connection %p", c);
if (client->userdata == NULL) {
crm_trace("Client hadn't registered with us yet");
} else if (strcasecmp(CRM_SYSTEM_PENGINE, client->userdata) == 0) {
the_subsystem = pe_subsystem;
} else if (strcasecmp(CRM_SYSTEM_TENGINE, client->userdata) == 0) {
the_subsystem = te_subsystem;
} else if (strcasecmp(CRM_SYSTEM_CIB, client->userdata) == 0) {
the_subsystem = cib_subsystem;
}
if (the_subsystem != NULL) {
the_subsystem->source = NULL;
the_subsystem->client = NULL;
crm_info("Received HUP from %s:[%d]", the_subsystem->name, the_subsystem->pid);
} else {
/* else that was a transient client */
crm_trace("Received HUP from transient client");
}
crm_trace("Disconnecting client %s (%p)", crm_client_name(client), client);
free(client->userdata);
crm_client_destroy(client);
trigger_fsa(fsa_source);
return 0;
}
static void
crmd_ipc_destroy(qb_ipcs_connection_t *c)
{
crm_trace("Connection %p", c);
}
/* A_STOP */
void
do_stop(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input, fsa_data_t * msg_data)
{
if (is_heartbeat_cluster()) {
stop_subsystem(pe_subsystem, FALSE);
}
mainloop_del_ipc_server(ipcs);
register_fsa_input(C_FSA_INTERNAL, I_TERMINATE, NULL);
}
/* A_STARTED */
void
do_started(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)
{
static struct qb_ipcs_service_handlers crmd_callbacks =
{
.connection_accept = crmd_ipc_accept,
.connection_created = crmd_ipc_created,
.msg_process = crmd_ipc_dispatch,
.connection_closed = crmd_ipc_closed,
.connection_destroyed = crmd_ipc_destroy
};
if (cur_state != S_STARTING) {
crm_err("Start cancelled... %s", fsa_state2string(cur_state));
return;
} else if (is_set(fsa_input_register, R_MEMBERSHIP) == FALSE) {
crm_info("Delaying start, no membership data (%.16llx)", R_MEMBERSHIP);
crmd_fsa_stall(TRUE);
return;
} else if (is_set(fsa_input_register, R_LRM_CONNECTED) == FALSE) {
crm_info("Delaying start, LRM not connected (%.16llx)", R_LRM_CONNECTED);
crmd_fsa_stall(TRUE);
return;
} else if (is_set(fsa_input_register, R_CIB_CONNECTED) == FALSE) {
crm_info("Delaying start, CIB not connected (%.16llx)", R_CIB_CONNECTED);
crmd_fsa_stall(TRUE);
return;
} else if (is_set(fsa_input_register, R_READ_CONFIG) == FALSE) {
crm_info("Delaying start, Config not read (%.16llx)", R_READ_CONFIG);
crmd_fsa_stall(TRUE);
return;
} else if (is_set(fsa_input_register, R_PEER_DATA) == FALSE) {
/* try reading from HA */
crm_info("Delaying start, No peer data (%.16llx)", R_PEER_DATA);
#if SUPPORT_HEARTBEAT
if (is_heartbeat_cluster()) {
HA_Message *msg = NULL;
crm_trace("Looking for a HA message");
msg = fsa_cluster_conn->llc_ops->readmsg(fsa_cluster_conn, 0);
if (msg != NULL) {
crm_trace("There was a HA message");
ha_msg_del(msg);
}
}
#endif
crmd_fsa_stall(TRUE);
return;
}
crm_debug("Init server comms");
ipcs = mainloop_add_ipc_server(CRM_SYSTEM_CRMD, QB_IPC_NATIVE, &crmd_callbacks);
if (ipcs == NULL) {
crm_err("Failed to create IPC server: shutting down and inhibiting respawn");
register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
}
if (stonith_reconnect == NULL) {
int dummy;
stonith_reconnect = mainloop_add_trigger(G_PRIORITY_LOW, te_connect_stonith, &dummy);
}
set_bit(fsa_input_register, R_ST_REQUIRED);
mainloop_set_trigger(stonith_reconnect);
crm_notice("The local CRM is operational");
clear_bit(fsa_input_register, R_STARTING);
register_fsa_input(msg_data->fsa_cause, I_PENDING, NULL);
}
/* A_RECOVER */
void
do_recover(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)
{
set_bit(fsa_input_register, R_IN_RECOVERY);
crm_err("Action %s (%.16llx) not supported", fsa_action2string(action), action);
register_fsa_input(C_FSA_INTERNAL, I_TERMINATE, NULL);
}
/* *INDENT-OFF* */
pe_cluster_option crmd_opts[] = {
/* name, old-name, validate, default, description */
{ "dc-version", NULL, "string", NULL, "none", NULL, "Version of Pacemaker on the cluster's DC.", "Includes the hash which identifies the exact Mercurial changeset it was built from. Used for diagnostic purposes." },
{ "cluster-infrastructure", NULL, "string", NULL, "heartbeat", NULL, "The messaging stack on which Pacemaker is currently running.", "Used for informational and diagnostic purposes." },
{ XML_CONFIG_ATTR_DC_DEADTIME, "dc_deadtime", "time", NULL, "20s", &check_time, "How long to wait for a response from other nodes during startup.", "The \"correct\" value will depend on the speed/load of your network and the type of switches used." },
{ XML_CONFIG_ATTR_RECHECK, "cluster_recheck_interval", "time",
"Zero disables polling. Positive values are an interval in seconds (unless other SI units are specified. eg. 5min)", "15min", &check_timer,
"Polling interval for time based changes to options, resource parameters and constraints.",
"The Cluster is primarily event driven, however the configuration can have elements that change based on time."
" To ensure these changes take effect, we can optionally poll the cluster's status for changes." },
{ XML_CONFIG_ATTR_ELECTION_FAIL, "election_timeout", "time", NULL, "2min", &check_timer, "*** Advanced Use Only ***.", "If need to adjust this value, it probably indicates the presence of a bug." },
{ XML_CONFIG_ATTR_FORCE_QUIT, "shutdown_escalation", "time", NULL, "20min", &check_timer, "*** Advanced Use Only ***.", "If need to adjust this value, it probably indicates the presence of a bug." },
{ "crmd-integration-timeout", NULL, "time", NULL, "3min", &check_timer, "*** Advanced Use Only ***.", "If need to adjust this value, it probably indicates the presence of a bug." },
{ "crmd-finalization-timeout", NULL, "time", NULL, "30min", &check_timer, "*** Advanced Use Only ***.", "If you need to adjust this value, it probably indicates the presence of a bug." },
{ "crmd-transition-delay", NULL, "time", NULL, "0s", &check_timer, "*** Advanced Use Only ***\nEnabling this option will slow down cluster recovery under all conditions", "Delay cluster recovery for the configured interval to allow for additional/related events to occur.\nUseful if your configuration is sensitive to the order in which ping updates arrive." },
{ XML_ATTR_EXPECTED_VOTES, NULL, "integer", NULL, "2", &check_number, "The number of nodes expected to be in the cluster", "Used to calculate quorum in openais based clusters." },
};
/* *INDENT-ON* */
void
crmd_metadata(void)
{
config_metadata("CRM Daemon", "1.0",
"CRM Daemon Options",
"This is a fake resource that details the options that can be configured for the CRM Daemon.",
crmd_opts, DIMOF(crmd_opts));
}
static void
verify_crmd_options(GHashTable * options)
{
verify_all_options(options, crmd_opts, DIMOF(crmd_opts));
}
static const char *
crmd_pref(GHashTable * options, const char *name)
{
return get_cluster_pref(options, crmd_opts, DIMOF(crmd_opts), name);
}
static void
config_query_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
{
const char *value = NULL;
GHashTable *config_hash = NULL;
crm_time_t *now = crm_time_new(NULL);
if (rc != pcmk_ok) {
fsa_data_t *msg_data = NULL;
crm_err("Local CIB query resulted in an error: %s", pcmk_strerror(rc));
register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
if (rc == -EACCES || rc == -pcmk_err_dtd_validation) {
crm_err("The cluster is mis-configured - shutting down and staying down");
set_bit(fsa_input_register, R_STAYDOWN);
}
goto bail;
}
crm_debug("Call %d : Parsing CIB options", call_id);
config_hash =
g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str);
unpack_instance_attributes(output, output, XML_CIB_TAG_PROPSET, NULL, config_hash,
CIB_OPTIONS_FIRST, FALSE, now);
verify_crmd_options(config_hash);
value = crmd_pref(config_hash, XML_CONFIG_ATTR_DC_DEADTIME);
election_trigger->period_ms = crm_get_msec(value);
value = crmd_pref(config_hash, XML_CONFIG_ATTR_FORCE_QUIT);
shutdown_escalation_timer->period_ms = crm_get_msec(value);
crm_debug("Shutdown escalation occurs after: %dms", shutdown_escalation_timer->period_ms);
value = crmd_pref(config_hash, XML_CONFIG_ATTR_ELECTION_FAIL);
election_timeout->period_ms = crm_get_msec(value);
value = crmd_pref(config_hash, XML_CONFIG_ATTR_RECHECK);
recheck_timer->period_ms = crm_get_msec(value);
crm_debug("Checking for expired actions every %dms", recheck_timer->period_ms);
value = crmd_pref(config_hash, "crmd-transition-delay");
transition_timer->period_ms = crm_get_msec(value);
value = crmd_pref(config_hash, "crmd-integration-timeout");
integration_timer->period_ms = crm_get_msec(value);
value = crmd_pref(config_hash, "crmd-finalization-timeout");
finalization_timer->period_ms = crm_get_msec(value);
#if SUPPORT_COROSYNC
if (is_classic_ais_cluster()) {
value = crmd_pref(config_hash, XML_ATTR_EXPECTED_VOTES);
crm_debug("Sending expected-votes=%s to corosync", value);
send_ais_text(crm_class_quorum, value, TRUE, NULL, crm_msg_ais);
}
#endif
set_bit(fsa_input_register, R_READ_CONFIG);
crm_trace("Triggering FSA: %s", __FUNCTION__);
mainloop_set_trigger(fsa_source);
g_hash_table_destroy(config_hash);
bail:
crm_time_free(now);
}
gboolean
crm_read_options(gpointer user_data)
{
int call_id =
fsa_cib_conn->cmds->query(fsa_cib_conn, XML_CIB_TAG_CRMCONFIG, NULL, cib_scope_local);
fsa_register_cib_callback(call_id, FALSE, NULL, config_query_callback);
crm_trace("Querying the CIB... call %d", call_id);
return TRUE;
}
/* A_READCONFIG */
void
do_read_config(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)
{
mainloop_set_trigger(config_read);
}
void
crm_shutdown(int nsig)
{
if (crmd_mainloop != NULL && g_main_is_running(crmd_mainloop)) {
if (is_set(fsa_input_register, R_SHUTDOWN)) {
crm_err("Escalating the shutdown");
register_fsa_input_before(C_SHUTDOWN, I_ERROR, NULL);
} else {
set_bit(fsa_input_register, R_SHUTDOWN);
register_fsa_input(C_SHUTDOWN, I_SHUTDOWN, NULL);
if (shutdown_escalation_timer->period_ms < 1) {
const char *value = crmd_pref(NULL, XML_CONFIG_ATTR_FORCE_QUIT);
int msec = crm_get_msec(value);
crm_debug("Using default shutdown escalation: %dms", msec);
shutdown_escalation_timer->period_ms = msec;
}
/* cant rely on this... */
crm_notice("Requesting shutdown, upper limit is %dms", shutdown_escalation_timer->period_ms);
crm_timer_start(shutdown_escalation_timer);
}
} else {
crm_info("exit from shutdown");
crmd_exit(EX_OK);
}
}
diff --git a/crmd/crmd_callbacks.h b/crmd/crmd_callbacks.h
index f844729833..2fc944cd6c 100644
--- a/crmd/crmd_callbacks.h
+++ b/crmd/crmd_callbacks.h
@@ -1,47 +1,45 @@
/*
* 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 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <crm/cluster.h>
extern void crmd_ha_msg_filter(xmlNode * msg);
/*
* Apparently returning TRUE means "stay connected, keep doing stuff".
* Returning FALSE means "we're all done, close the connection"
*/
extern void crmd_ipc_connection_destroy(gpointer user_data);
-extern void lrm_op_callback(lrmd_event_data_t * op);
-
extern void crmd_ha_status_callback(const char *node, const char *status, void *private_data);
extern void crmd_client_status_callback(const char *node, const char *client, const char *status,
void *private);
extern void msg_ccm_join(const xmlNode * msg, void *foo);
extern void crmd_cib_connection_destroy(gpointer user_data);
extern gboolean crm_fsa_trigger(gpointer user_data);
extern void peer_update_callback(enum crm_status_type type, crm_node_t * node, const void *data);
#if SUPPORT_HEARTBEAT
void crmd_ha_msg_callback(HA_Message * hamsg, void *private_data);
#endif
diff --git a/crmd/crmd_fsa.h b/crmd/crmd_fsa.h
index 164e524744..37de62a7f7 100644
--- a/crmd/crmd_fsa.h
+++ b/crmd/crmd_fsa.h
@@ -1,135 +1,134 @@
/*
* 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 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef CRMD_FSA__H
# define CRMD_FSA__H
# include <fsa_defines.h>
# include <crm/crm.h>
# include <crm/cib.h>
# include <crm/common/xml.h>
# include <crm/common/mainloop.h>
# include <crm/cluster.h>
# include <crm/common/ipc.h>
# if SUPPORT_HEARTBEAT
extern ll_cluster_t *fsa_cluster_conn;
# endif
/* copy from struct client_child in heartbeat.h
*
* Plus a couple of other things
*/
struct crm_subsystem_s {
pid_t pid; /* Process id of child process */
const char *name; /* executable name */
const char *path; /* Command location */
const char *command; /* Command with path */
const char *args; /* Command arguments */
crm_client_t *client; /* Client connection object */
gboolean sent_kill;
mainloop_io_t *source; /* How can we communicate with it */
long long flag_connected; /* */
long long flag_required; /* */
};
typedef struct fsa_timer_s fsa_timer_t;
struct fsa_timer_s {
guint source_id; /* timer source id */
int period_ms; /* timer period */
enum crmd_fsa_input fsa_input;
gboolean(*callback) (gpointer data);
gboolean repeat;
int counter;
};
enum fsa_data_type {
fsa_dt_none,
fsa_dt_ha_msg,
fsa_dt_xml,
fsa_dt_lrm,
};
typedef struct fsa_data_s fsa_data_t;
struct fsa_data_s {
int id;
enum crmd_fsa_input fsa_input;
enum crmd_fsa_cause fsa_cause;
long long actions;
const char *origin;
void *data;
enum fsa_data_type data_type;
};
extern enum crmd_fsa_state s_crmd_fsa(enum crmd_fsa_cause cause);
/* Global FSA stuff */
extern volatile gboolean do_fsa_stall;
extern volatile enum crmd_fsa_state fsa_state;
extern volatile long long fsa_input_register;
extern volatile long long fsa_actions;
-extern lrmd_t *fsa_lrm_conn;
extern cib_t *fsa_cib_conn;
extern char *fsa_our_uname;
extern char *fsa_our_uuid;
extern char *fsa_pe_ref; /* the last invocation of the PE */
extern char *fsa_our_dc;
extern char *fsa_our_dc_version;
extern GListPtr fsa_message_queue;
extern fsa_timer_t *election_trigger; /* */
extern fsa_timer_t *election_timeout; /* */
extern fsa_timer_t *shutdown_escalation_timer; /* */
extern fsa_timer_t *transition_timer;
extern fsa_timer_t *integration_timer;
extern fsa_timer_t *finalization_timer;
extern fsa_timer_t *wait_timer;
extern fsa_timer_t *recheck_timer;
extern crm_trigger_t *fsa_source;
extern crm_trigger_t *config_read;
extern struct crm_subsystem_s *cib_subsystem;
extern struct crm_subsystem_s *te_subsystem;
extern struct crm_subsystem_s *pe_subsystem;
extern GHashTable *welcomed_nodes;
extern GHashTable *integrated_nodes;
extern GHashTable *finalized_nodes;
extern GHashTable *confirmed_nodes;
extern GHashTable *crmd_peer_state;
/* these two should be moved elsewhere... */
extern void do_update_cib_nodes(gboolean overwrite, const char *caller);
extern gboolean do_dc_heartbeat(gpointer data);
# define AM_I_DC is_set(fsa_input_register, R_THE_DC)
# define AM_I_OPERATIONAL (is_set(fsa_input_register, R_STARTING)==FALSE)
extern unsigned long long saved_ccm_membership_id;
extern gboolean ever_had_quorum;
# include <fsa_proto.h>
# include <crmd_utils.h>
#define trigger_fsa(source) crm_trace("Triggering FSA: %s", __FUNCTION__); \
mainloop_set_trigger(source);
#endif
diff --git a/crmd/crmd_lrm.h b/crmd/crmd_lrm.h
index a425c76bf2..8941fce734 100644
--- a/crmd/crmd_lrm.h
+++ b/crmd/crmd_lrm.h
@@ -1,20 +1,100 @@
/*
* 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 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
extern gboolean verify_stopped(enum crmd_fsa_state cur_state, int log_level);
extern void lrm_clear_last_failure(const char *rsc_id);
+void lrm_op_callback(lrmd_event_data_t * op);
+
+typedef struct resource_history_s {
+ char *id;
+ lrmd_rsc_info_t rsc;
+ lrmd_event_data_t *last;
+ lrmd_event_data_t *failed;
+ GList *recurring_op_list;
+
+ /* Resources must be stopped using the same
+ * parameters they were started with. This hashtable
+ * holds the parameters that should be used for the next stop
+ * cmd on this resource. */
+ GHashTable *stop_params;
+} rsc_history_t;
+
+struct recurring_op_s {
+ char *rsc_id;
+ char *op_type;
+ char *op_key;
+ int call_id;
+ int interval;
+ gboolean remove;
+ gboolean cancelled;
+};
+
+typedef struct lrm_state_s {
+ const char *node_name;
+ lrmd_t *conn;
+
+ GHashTable *resource_history;
+ GHashTable *pending_ops;
+ GHashTable *deletion_ops;
+
+ int num_lrm_register_fails;
+} lrm_state_t;
+
+struct pending_deletion_op_s {
+ char *rsc;
+ ha_msg_input_t *input;
+};
+
+/*!
+ * \brief Clear all state information from a single state entry.
+ * \note This does not close the lrmd connection
+ */
+void lrm_state_reset_tables(lrm_state_t *lrm_state);
+GList *lrm_state_get_list(void);
+
+/*!
+ * \brief Initiate internal state tables
+ */
+gboolean lrm_state_init_local(void);
+
+/*!
+ * \brief Destroy all state entries and internal state tables
+ */
+void lrm_state_destroy_all(void);
+
+/*!
+ * \brief Create remote lrmd connection entry.
+ */
+lrm_state_t *lrm_state_create_remote(const char *node_name, const char *server, int port);
+
+/*!
+ * \brief Destroy lrmd connection keyed of node name
+ */
+void lrm_state_destroy(const char *node_name);
+
+/*!
+ * \brief Find lrm_state data by node name
+ */
+lrm_state_t *lrm_state_find(const char *node_name);
+
+/*!
+ * \brief Either find or create a new entry for the local
+ * ipc lrmd connection.
+ */
+lrm_state_t *lrm_state_find_or_create_local(const char *node_name);
+
diff --git a/crmd/crmd_messages.h b/crmd/crmd_messages.h
index 479b8b4087..a4837bbd53 100644
--- a/crmd/crmd_messages.h
+++ b/crmd/crmd_messages.h
@@ -1,113 +1,111 @@
/*
* 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 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef XML_CRM_MESSAGES__H
# define XML_CRM_MESSAGES__H
# include <crm/crm.h>
# include <crm/common/ipc.h>
# include <crm/common/xml.h>
# include <crm/cluster/internal.h>
# include <crmd_fsa.h>
typedef struct ha_msg_input_s {
xmlNode *msg;
xmlNode *xml;
} ha_msg_input_t;
extern ha_msg_input_t *new_ha_msg_input(xmlNode * orig);
extern void delete_ha_msg_input(ha_msg_input_t * orig);
extern void *fsa_typed_data_adv(fsa_data_t * fsa_data, enum fsa_data_type a_type,
const char *caller);
# define fsa_typed_data(x) fsa_typed_data_adv(msg_data, x, __FUNCTION__)
extern void register_fsa_error_adv(enum crmd_fsa_cause cause, enum crmd_fsa_input input,
fsa_data_t * cur_data, void *new_data, const char *raised_from);
# define register_fsa_error(cause, input, new_data) register_fsa_error_adv(cause, input, msg_data, new_data, __FUNCTION__)
extern int register_fsa_input_adv(enum crmd_fsa_cause cause, enum crmd_fsa_input input,
void *data, long long with_actions,
gboolean prepend, const char *raised_from);
extern void fsa_dump_queue(int log_level);
extern void route_message(enum crmd_fsa_cause cause, xmlNode * input);
# define crmd_fsa_stall(suppress) do { \
if(suppress == FALSE && msg_data != NULL) { \
register_fsa_input_adv( \
((fsa_data_t*)msg_data)->fsa_cause, I_WAIT_FOR_EVENT, \
((fsa_data_t*)msg_data)->data, action, TRUE, __FUNCTION__); \
} else { \
register_fsa_input_adv( \
C_FSA_INTERNAL, I_WAIT_FOR_EVENT, \
NULL, action, TRUE, __FUNCTION__); \
} \
} while(0)
# define register_fsa_input(cause, input, data) register_fsa_input_adv(cause, input, data, A_NOTHING, FALSE, __FUNCTION__)
# define register_fsa_action(action) { \
fsa_actions |= action; \
if(fsa_source) { \
mainloop_set_trigger(fsa_source); \
} \
crm_debug("%s added action %s to the FSA", \
__FUNCTION__, fsa_action2string(action)); \
}
# define register_fsa_input_before(cause, input, data) register_fsa_input_adv(cause, input, data, A_NOTHING, TRUE, __FUNCTION__)
# define register_fsa_input_later(cause, input, data) register_fsa_input_adv(cause, input, data, A_NOTHING, FALSE, __FUNCTION__)
void delete_fsa_input(fsa_data_t * fsa_data);
GListPtr put_message(fsa_data_t * new_message);
fsa_data_t *get_message(void);
gboolean is_message(void);
gboolean have_wait_message(void);
extern gboolean relay_message(xmlNode * relay_message, gboolean originated_locally);
extern void process_message(xmlNode * msg, gboolean originated_locally, const char *src_node_name);
extern gboolean crm_dc_process_message(xmlNode * whole_message,
xmlNode * action,
const char *host_from,
const char *sys_from,
const char *sys_to, const char *op, gboolean dc_mode);
extern gboolean send_msg_via_ipc(xmlNode * msg, const char *sys);
extern gboolean add_pending_outgoing_reply(const char *originating_node_name,
const char *crm_msg_reference,
const char *sys_to, const char *sys_from);
extern gboolean crmd_authorize_message(xmlNode * client_msg, crm_client_t * curr_client);
extern gboolean send_request(xmlNode * msg, char **msg_reference);
extern enum crmd_fsa_input handle_message(xmlNode * stored_msg);
-extern void lrm_op_callback(lrmd_event_data_t * op);
-
extern ha_msg_input_t *copy_ha_msg_input(ha_msg_input_t * orig);
#endif
diff --git a/crmd/fsa.c b/crmd/fsa.c
index ccefd4d094..9cd5beb8bc 100644
--- a/crmd/fsa.c
+++ b/crmd/fsa.c
@@ -1,685 +1,684 @@
/*
* 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 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <crm_internal.h>
#include <sys/param.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <crm/crm.h>
#include <crm/lrmd.h>
#include <crm/cib.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/cluster.h>
#include <crmd_messages.h>
#include <crmd_fsa.h>
#include <tengine.h>
#include <fsa_proto.h>
#include <fsa_matrix.h>
char *fsa_our_dc = NULL;
cib_t *fsa_cib_conn = NULL;
char *fsa_our_dc_version = NULL;
-lrmd_t *fsa_lrm_conn;
char *fsa_our_uuid = NULL;
char *fsa_our_uname = NULL;
#if SUPPORT_HEARTBEAT
ll_cluster_t *fsa_cluster_conn;
#endif
fsa_timer_t *wait_timer = NULL;
fsa_timer_t *recheck_timer = NULL;
fsa_timer_t *election_trigger = NULL;
fsa_timer_t *election_timeout = NULL;
fsa_timer_t *transition_timer = NULL;
fsa_timer_t *integration_timer = NULL;
fsa_timer_t *finalization_timer = NULL;
fsa_timer_t *shutdown_escalation_timer = NULL;
volatile gboolean do_fsa_stall = FALSE;
volatile long long fsa_input_register = 0;
volatile long long fsa_actions = A_NOTHING;
volatile enum crmd_fsa_state fsa_state = S_STARTING;
extern uint highest_born_on;
extern uint num_join_invites;
extern GHashTable *welcomed_nodes;
extern GHashTable *finalized_nodes;
extern GHashTable *confirmed_nodes;
extern GHashTable *integrated_nodes;
extern void initialize_join(gboolean before);
#define DOT_PREFIX "actions:trace: "
#define do_dot_log(fmt, args...) crm_trace( fmt, ##args)
long long do_state_transition(long long actions,
enum crmd_fsa_state cur_state,
enum crmd_fsa_state next_state, fsa_data_t * msg_data);
void dump_rsc_info(void);
void dump_rsc_info_callback(const xmlNode * msg, int call_id, int rc,
xmlNode * output, void *user_data);
void ghash_print_node(gpointer key, gpointer value, gpointer user_data);
void s_crmd_fsa_actions(fsa_data_t * fsa_data);
void log_fsa_input(fsa_data_t * stored_msg);
void init_dotfile(void);
void
init_dotfile(void)
{
do_dot_log(DOT_PREFIX "digraph \"g\" {");
do_dot_log(DOT_PREFIX " size = \"30,30\"");
do_dot_log(DOT_PREFIX " graph [");
do_dot_log(DOT_PREFIX " fontsize = \"12\"");
do_dot_log(DOT_PREFIX " fontname = \"Times-Roman\"");
do_dot_log(DOT_PREFIX " fontcolor = \"black\"");
do_dot_log(DOT_PREFIX " bb = \"0,0,398.922306,478.927856\"");
do_dot_log(DOT_PREFIX " color = \"black\"");
do_dot_log(DOT_PREFIX " ]");
do_dot_log(DOT_PREFIX " node [");
do_dot_log(DOT_PREFIX " fontsize = \"12\"");
do_dot_log(DOT_PREFIX " fontname = \"Times-Roman\"");
do_dot_log(DOT_PREFIX " fontcolor = \"black\"");
do_dot_log(DOT_PREFIX " shape = \"ellipse\"");
do_dot_log(DOT_PREFIX " color = \"black\"");
do_dot_log(DOT_PREFIX " ]");
do_dot_log(DOT_PREFIX " edge [");
do_dot_log(DOT_PREFIX " fontsize = \"12\"");
do_dot_log(DOT_PREFIX " fontname = \"Times-Roman\"");
do_dot_log(DOT_PREFIX " fontcolor = \"black\"");
do_dot_log(DOT_PREFIX " color = \"black\"");
do_dot_log(DOT_PREFIX " ]");
do_dot_log(DOT_PREFIX "// special nodes");
do_dot_log(DOT_PREFIX " \"S_PENDING\" ");
do_dot_log(DOT_PREFIX " [");
do_dot_log(DOT_PREFIX " color = \"blue\"");
do_dot_log(DOT_PREFIX " fontcolor = \"blue\"");
do_dot_log(DOT_PREFIX " ]");
do_dot_log(DOT_PREFIX " \"S_TERMINATE\" ");
do_dot_log(DOT_PREFIX " [");
do_dot_log(DOT_PREFIX " color = \"red\"");
do_dot_log(DOT_PREFIX " fontcolor = \"red\"");
do_dot_log(DOT_PREFIX " ]");
do_dot_log(DOT_PREFIX "// DC only nodes");
do_dot_log(DOT_PREFIX " \"S_INTEGRATION\" [ fontcolor = \"green\" ]");
do_dot_log(DOT_PREFIX " \"S_POLICY_ENGINE\" [ fontcolor = \"green\" ]");
do_dot_log(DOT_PREFIX " \"S_TRANSITION_ENGINE\" [ fontcolor = \"green\" ]");
do_dot_log(DOT_PREFIX " \"S_RELEASE_DC\" [ fontcolor = \"green\" ]");
do_dot_log(DOT_PREFIX " \"S_IDLE\" [ fontcolor = \"green\" ]");
}
static void
do_fsa_action(fsa_data_t * fsa_data, long long an_action,
void (*function) (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))
{
fsa_actions &= ~an_action;
crm_trace(DOT_PREFIX "\t// %s", fsa_action2string(an_action));
function(an_action, fsa_data->fsa_cause, fsa_state, fsa_data->fsa_input, fsa_data);
}
static long long startup_actions =
A_STARTUP | A_CIB_START | A_LRM_CONNECT | A_CCM_CONNECT | A_HA_CONNECT | A_READCONFIG |
A_STARTED | A_CL_JOIN_QUERY;
enum crmd_fsa_state
s_crmd_fsa(enum crmd_fsa_cause cause)
{
fsa_data_t *fsa_data = NULL;
long long register_copy = fsa_input_register;
long long new_actions = A_NOTHING;
enum crmd_fsa_state last_state = fsa_state;
crm_trace("FSA invoked with Cause: %s\tState: %s",
fsa_cause2string(cause), fsa_state2string(fsa_state));
fsa_dump_actions(fsa_actions, "Initial");
do_fsa_stall = FALSE;
if (is_message() == FALSE && fsa_actions != A_NOTHING) {
/* fake the first message so we can get into the loop */
fsa_data = calloc(1, sizeof(fsa_data_t));
fsa_data->fsa_input = I_NULL;
fsa_data->fsa_cause = C_FSA_INTERNAL;
fsa_data->origin = __FUNCTION__;
fsa_data->data_type = fsa_dt_none;
fsa_message_queue = g_list_append(fsa_message_queue, fsa_data);
fsa_data = NULL;
}
while (is_message() && do_fsa_stall == FALSE) {
crm_trace("Checking messages (%d remaining)", g_list_length(fsa_message_queue));
fsa_data = get_message();
CRM_CHECK(fsa_data != NULL, continue);
log_fsa_input(fsa_data);
/* add any actions back to the queue */
fsa_actions |= fsa_data->actions;
fsa_dump_actions(fsa_data->actions, "Restored actions");
/* get the next batch of actions */
new_actions = crmd_fsa_actions[fsa_data->fsa_input][fsa_state];
fsa_actions |= new_actions;
fsa_dump_actions(new_actions, "New actions");
if (fsa_data->fsa_input != I_NULL && fsa_data->fsa_input != I_ROUTER) {
crm_debug("Processing %s: [ state=%s cause=%s origin=%s ]",
fsa_input2string(fsa_data->fsa_input),
fsa_state2string(fsa_state),
fsa_cause2string(fsa_data->fsa_cause), fsa_data->origin);
}
/* logging : *before* the state is changed */
if (is_set(fsa_actions, A_ERROR)) {
do_fsa_action(fsa_data, A_ERROR, do_log);
}
if (is_set(fsa_actions, A_WARN)) {
do_fsa_action(fsa_data, A_WARN, do_log);
}
if (is_set(fsa_actions, A_LOG)) {
do_fsa_action(fsa_data, A_LOG, do_log);
}
/* update state variables */
last_state = fsa_state;
fsa_state = crmd_fsa_state[fsa_data->fsa_input][fsa_state];
/*
* Remove certain actions during shutdown
*/
if (fsa_state == S_STOPPING || ((fsa_input_register & R_SHUTDOWN) == R_SHUTDOWN)) {
clear_bit(fsa_actions, startup_actions);
}
/*
* Hook for change of state.
* Allows actions to be added or removed when entering a state
*/
if (last_state != fsa_state) {
fsa_actions = do_state_transition(fsa_actions, last_state, fsa_state, fsa_data);
} else {
do_dot_log(DOT_PREFIX "\t// FSA input: State=%s \tCause=%s"
" \tInput=%s \tOrigin=%s() \tid=%d",
fsa_state2string(fsa_state),
fsa_cause2string(fsa_data->fsa_cause),
fsa_input2string(fsa_data->fsa_input), fsa_data->origin, fsa_data->id);
}
/* start doing things... */
s_crmd_fsa_actions(fsa_data);
delete_fsa_input(fsa_data);
fsa_data = NULL;
}
if (g_list_length(fsa_message_queue) > 0 || fsa_actions != A_NOTHING || do_fsa_stall) {
crm_debug("Exiting the FSA: queue=%d, fsa_actions=0x%llx, stalled=%s",
g_list_length(fsa_message_queue), fsa_actions, do_fsa_stall ? "true" : "false");
} else {
crm_trace("Exiting the FSA");
}
/* cleanup inputs? */
if (register_copy != fsa_input_register) {
long long same = register_copy & fsa_input_register;
fsa_dump_inputs(LOG_DEBUG, "Added", fsa_input_register ^ same);
fsa_dump_inputs(LOG_DEBUG, "Removed", register_copy ^ same);
}
fsa_dump_actions(fsa_actions, "Remaining");
fsa_dump_queue(LOG_DEBUG);
return fsa_state;
}
void
s_crmd_fsa_actions(fsa_data_t * fsa_data)
{
/*
* Process actions in order of priority but do only one
* action at a time to avoid complicating the ordering.
*/
CRM_CHECK(fsa_data != NULL, return);
while (fsa_actions != A_NOTHING && do_fsa_stall == FALSE) {
/* regular action processing in order of action priority
*
* Make sure all actions that connect to required systems
* are performed first
*/
if (fsa_actions & A_ERROR) {
do_fsa_action(fsa_data, A_ERROR, do_log);
} else if (fsa_actions & A_WARN) {
do_fsa_action(fsa_data, A_WARN, do_log);
} else if (fsa_actions & A_LOG) {
do_fsa_action(fsa_data, A_LOG, do_log);
/* get out of here NOW! before anything worse happens */
} else if (fsa_actions & A_EXIT_1) {
do_fsa_action(fsa_data, A_EXIT_1, do_exit);
/* sub-system restart */
} else if ((fsa_actions & O_LRM_RECONNECT) == O_LRM_RECONNECT) {
do_fsa_action(fsa_data, O_LRM_RECONNECT, do_lrm_control);
} else if ((fsa_actions & O_CIB_RESTART) == O_CIB_RESTART) {
do_fsa_action(fsa_data, O_CIB_RESTART, do_cib_control);
} else if ((fsa_actions & O_PE_RESTART) == O_PE_RESTART) {
do_fsa_action(fsa_data, O_PE_RESTART, do_pe_control);
} else if ((fsa_actions & O_TE_RESTART) == O_TE_RESTART) {
do_fsa_action(fsa_data, O_TE_RESTART, do_te_control);
/* essential start tasks */
} else if (fsa_actions & A_STARTUP) {
do_fsa_action(fsa_data, A_STARTUP, do_startup);
} else if (fsa_actions & A_CIB_START) {
do_fsa_action(fsa_data, A_CIB_START, do_cib_control);
} else if (fsa_actions & A_HA_CONNECT) {
do_fsa_action(fsa_data, A_HA_CONNECT, do_ha_control);
} else if (fsa_actions & A_READCONFIG) {
do_fsa_action(fsa_data, A_READCONFIG, do_read_config);
/* sub-system start/connect */
} else if (fsa_actions & A_LRM_CONNECT) {
do_fsa_action(fsa_data, A_LRM_CONNECT, do_lrm_control);
} else if (fsa_actions & A_CCM_CONNECT) {
#if SUPPORT_HEARTBEAT
if (is_heartbeat_cluster()) {
do_fsa_action(fsa_data, A_CCM_CONNECT, do_ccm_control);
}
#endif
fsa_actions &= ~A_CCM_CONNECT;
} else if (fsa_actions & A_TE_START) {
do_fsa_action(fsa_data, A_TE_START, do_te_control);
} else if (fsa_actions & A_PE_START) {
do_fsa_action(fsa_data, A_PE_START, do_pe_control);
/* Timers */
/* else if(fsa_actions & O_DC_TIMER_RESTART) {
do_fsa_action(fsa_data, O_DC_TIMER_RESTART, do_timer_control) */ ;
} else if (fsa_actions & A_DC_TIMER_STOP) {
do_fsa_action(fsa_data, A_DC_TIMER_STOP, do_timer_control);
} else if (fsa_actions & A_INTEGRATE_TIMER_STOP) {
do_fsa_action(fsa_data, A_INTEGRATE_TIMER_STOP, do_timer_control);
} else if (fsa_actions & A_INTEGRATE_TIMER_START) {
do_fsa_action(fsa_data, A_INTEGRATE_TIMER_START, do_timer_control);
} else if (fsa_actions & A_FINALIZE_TIMER_STOP) {
do_fsa_action(fsa_data, A_FINALIZE_TIMER_STOP, do_timer_control);
} else if (fsa_actions & A_FINALIZE_TIMER_START) {
do_fsa_action(fsa_data, A_FINALIZE_TIMER_START, do_timer_control);
/*
* Highest priority actions
*/
} else if (fsa_actions & A_MSG_ROUTE) {
do_fsa_action(fsa_data, A_MSG_ROUTE, do_msg_route);
} else if (fsa_actions & A_RECOVER) {
do_fsa_action(fsa_data, A_RECOVER, do_recover);
} else if (fsa_actions & A_CL_JOIN_RESULT) {
do_fsa_action(fsa_data, A_CL_JOIN_RESULT, do_cl_join_finalize_respond);
} else if (fsa_actions & A_CL_JOIN_REQUEST) {
do_fsa_action(fsa_data, A_CL_JOIN_REQUEST, do_cl_join_offer_respond);
} else if (fsa_actions & A_SHUTDOWN_REQ) {
do_fsa_action(fsa_data, A_SHUTDOWN_REQ, do_shutdown_req);
} else if (fsa_actions & A_ELECTION_VOTE) {
do_fsa_action(fsa_data, A_ELECTION_VOTE, do_election_vote);
} else if (fsa_actions & A_ELECTION_COUNT) {
do_fsa_action(fsa_data, A_ELECTION_COUNT, do_election_count_vote);
} else if (fsa_actions & A_LRM_EVENT) {
do_fsa_action(fsa_data, A_LRM_EVENT, do_lrm_event);
/*
* High priority actions
*/
} else if (fsa_actions & A_STARTED) {
do_fsa_action(fsa_data, A_STARTED, do_started);
} else if (fsa_actions & A_CL_JOIN_QUERY) {
do_fsa_action(fsa_data, A_CL_JOIN_QUERY, do_cl_join_query);
} else if (fsa_actions & A_DC_TIMER_START) {
do_fsa_action(fsa_data, A_DC_TIMER_START, do_timer_control);
/*
* Medium priority actions
*/
} else if (fsa_actions & A_DC_TAKEOVER) {
do_fsa_action(fsa_data, A_DC_TAKEOVER, do_dc_takeover);
} else if (fsa_actions & A_DC_RELEASE) {
do_fsa_action(fsa_data, A_DC_RELEASE, do_dc_release);
} else if (fsa_actions & A_DC_JOIN_FINAL) {
do_fsa_action(fsa_data, A_DC_JOIN_FINAL, do_dc_join_final);
} else if (fsa_actions & A_ELECTION_CHECK) {
do_fsa_action(fsa_data, A_ELECTION_CHECK, do_election_check);
} else if (fsa_actions & A_ELECTION_START) {
do_fsa_action(fsa_data, A_ELECTION_START, do_election_vote);
} else if (fsa_actions & A_TE_HALT) {
do_fsa_action(fsa_data, A_TE_HALT, do_te_invoke);
} else if (fsa_actions & A_TE_CANCEL) {
do_fsa_action(fsa_data, A_TE_CANCEL, do_te_invoke);
} else if (fsa_actions & A_DC_JOIN_OFFER_ALL) {
do_fsa_action(fsa_data, A_DC_JOIN_OFFER_ALL, do_dc_join_offer_all);
} else if (fsa_actions & A_DC_JOIN_OFFER_ONE) {
do_fsa_action(fsa_data, A_DC_JOIN_OFFER_ONE, do_dc_join_offer_all);
} else if (fsa_actions & A_DC_JOIN_PROCESS_REQ) {
do_fsa_action(fsa_data, A_DC_JOIN_PROCESS_REQ, do_dc_join_filter_offer);
} else if (fsa_actions & A_DC_JOIN_PROCESS_ACK) {
do_fsa_action(fsa_data, A_DC_JOIN_PROCESS_ACK, do_dc_join_ack);
/*
* Low(er) priority actions
* Make sure the CIB is always updated before invoking the
* PE, and the PE before the TE
*/
} else if (fsa_actions & A_DC_JOIN_FINALIZE) {
do_fsa_action(fsa_data, A_DC_JOIN_FINALIZE, do_dc_join_finalize);
} else if (fsa_actions & A_LRM_INVOKE) {
do_fsa_action(fsa_data, A_LRM_INVOKE, do_lrm_invoke);
} else if (fsa_actions & A_PE_INVOKE) {
do_fsa_action(fsa_data, A_PE_INVOKE, do_pe_invoke);
} else if (fsa_actions & A_TE_INVOKE) {
do_fsa_action(fsa_data, A_TE_INVOKE, do_te_invoke);
} else if (fsa_actions & A_CL_JOIN_ANNOUNCE) {
do_fsa_action(fsa_data, A_CL_JOIN_ANNOUNCE, do_cl_join_announce);
/* sub-system stop */
} else if (fsa_actions & A_DC_RELEASED) {
do_fsa_action(fsa_data, A_DC_RELEASED, do_dc_release);
} else if (fsa_actions & A_PE_STOP) {
do_fsa_action(fsa_data, A_PE_STOP, do_pe_control);
} else if (fsa_actions & A_TE_STOP) {
do_fsa_action(fsa_data, A_TE_STOP, do_te_control);
} else if (fsa_actions & A_SHUTDOWN) {
do_fsa_action(fsa_data, A_SHUTDOWN, do_shutdown);
} else if (fsa_actions & A_LRM_DISCONNECT) {
do_fsa_action(fsa_data, A_LRM_DISCONNECT, do_lrm_control);
} else if (fsa_actions & A_CCM_DISCONNECT) {
#if SUPPORT_HEARTBEAT
if (is_heartbeat_cluster()) {
do_fsa_action(fsa_data, A_CCM_DISCONNECT, do_ccm_control);
}
#endif
fsa_actions &= ~A_CCM_DISCONNECT;
} else if (fsa_actions & A_HA_DISCONNECT) {
do_fsa_action(fsa_data, A_HA_DISCONNECT, do_ha_control);
} else if (fsa_actions & A_CIB_STOP) {
do_fsa_action(fsa_data, A_CIB_STOP, do_cib_control);
} else if (fsa_actions & A_STOP) {
do_fsa_action(fsa_data, A_STOP, do_stop);
/* exit gracefully */
} else if (fsa_actions & A_EXIT_0) {
do_fsa_action(fsa_data, A_EXIT_0, do_exit);
/* Error checking and reporting */
} else {
crm_err("Action %s (0x%llx) not supported ",
fsa_action2string(fsa_actions), fsa_actions);
register_fsa_error_adv(C_FSA_INTERNAL, I_ERROR, fsa_data, NULL, __FUNCTION__);
}
}
}
void
log_fsa_input(fsa_data_t * stored_msg)
{
crm_trace("Processing queued input %d", stored_msg->id);
if (stored_msg->fsa_cause == C_CCM_CALLBACK) {
crm_trace("FSA processing CCM callback from %s", stored_msg->origin);
} else if (stored_msg->fsa_cause == C_LRM_OP_CALLBACK) {
crm_trace("FSA processing LRM callback from %s", stored_msg->origin);
} else if (stored_msg->data == NULL) {
crm_trace("FSA processing input from %s", stored_msg->origin);
} else {
ha_msg_input_t *ha_input = fsa_typed_data_adv(stored_msg, fsa_dt_ha_msg, __FUNCTION__);
crm_trace("FSA processing XML message from %s", stored_msg->origin);
crm_log_xml_trace(ha_input->xml, "FSA message data");
}
}
long long
do_state_transition(long long actions,
enum crmd_fsa_state cur_state,
enum crmd_fsa_state next_state, fsa_data_t * msg_data)
{
int level = LOG_INFO;
long long tmp = actions;
gboolean clear_recovery_bit = TRUE;
enum crmd_fsa_cause cause = msg_data->fsa_cause;
enum crmd_fsa_input current_input = msg_data->fsa_input;
const char *state_from = fsa_state2string(cur_state);
const char *state_to = fsa_state2string(next_state);
const char *input = fsa_input2string(current_input);
CRM_LOG_ASSERT(cur_state != next_state);
do_dot_log(DOT_PREFIX "\t%s -> %s [ label=%s cause=%s origin=%s ]",
state_from, state_to, input, fsa_cause2string(cause), msg_data->origin);
if(cur_state == S_IDLE || next_state == S_IDLE) {
level = LOG_NOTICE;
} else if(cur_state == S_NOT_DC || next_state == S_NOT_DC) {
level = LOG_NOTICE;
} else if(cur_state == S_ELECTION) {
level = LOG_NOTICE;
} else if(next_state == S_RECOVERY) {
level = LOG_WARNING;
}
do_crm_log(level, "State transition %s -> %s [ input=%s cause=%s origin=%s ]",
state_from, state_to, input, fsa_cause2string(cause), msg_data->origin);
/* the last two clauses might cause trouble later */
if (election_timeout != NULL && next_state != S_ELECTION && cur_state != S_RELEASE_DC) {
crm_timer_stop(election_timeout);
/* } else { */
/* crm_timer_start(election_timeout); */
}
#if 0
if ((fsa_input_register & R_SHUTDOWN)) {
set_bit(tmp, A_DC_TIMER_STOP);
}
#endif
if (next_state == S_INTEGRATION) {
set_bit(tmp, A_INTEGRATE_TIMER_START);
} else {
set_bit(tmp, A_INTEGRATE_TIMER_STOP);
}
if (next_state == S_FINALIZE_JOIN) {
set_bit(tmp, A_FINALIZE_TIMER_START);
} else {
set_bit(tmp, A_FINALIZE_TIMER_STOP);
}
if (next_state != S_PENDING) {
set_bit(tmp, A_DC_TIMER_STOP);
}
if (next_state != S_ELECTION) {
highest_born_on = 0;
}
if (next_state != S_IDLE) {
crm_timer_stop(recheck_timer);
}
if (cur_state == S_FINALIZE_JOIN && next_state == S_POLICY_ENGINE) {
populate_cib_nodes(node_update_quick|node_update_cluster|node_update_peer|node_update_join|node_update_expected, __FUNCTION__);
}
switch (next_state) {
case S_PENDING:
fsa_cib_conn->cmds->set_slave(fsa_cib_conn, cib_scope_local);
/* fall through */
case S_ELECTION:
crm_trace("Resetting our DC to NULL on transition to %s",
fsa_state2string(next_state));
update_dc(NULL);
break;
case S_NOT_DC:
election_trigger->counter = 0;
if(stonith_cleanup_list) {
GListPtr gIter = NULL;
for (gIter = stonith_cleanup_list; gIter != NULL; gIter = gIter->next) {
char *target = gIter->data;
crm_info("Purging %s from stonith cleanup list", target);
free(target);
}
g_list_free(stonith_cleanup_list);
stonith_cleanup_list = NULL;
}
if (is_set(fsa_input_register, R_SHUTDOWN)) {
crm_info("(Re)Issuing shutdown request now" " that we have a new DC");
set_bit(tmp, A_SHUTDOWN_REQ);
}
CRM_LOG_ASSERT(fsa_our_dc != NULL);
if (fsa_our_dc == NULL) {
crm_err("Reached S_NOT_DC without a DC" " being recorded");
}
break;
case S_RECOVERY:
clear_recovery_bit = FALSE;
break;
case S_FINALIZE_JOIN:
CRM_LOG_ASSERT(AM_I_DC);
if (cause == C_TIMER_POPPED) {
crm_warn("Progressed to state %s after %s",
fsa_state2string(next_state), fsa_cause2string(cause));
}
if (g_hash_table_size(welcomed_nodes) > 0) {
char *msg = strdup(" Welcome reply not received from");
crm_warn("%u cluster nodes failed to respond"
" to the join offer.", g_hash_table_size(welcomed_nodes));
g_hash_table_foreach(welcomed_nodes, ghash_print_node, msg);
free(msg);
} else {
crm_debug("All %d cluster nodes "
"responded to the join offer.", g_hash_table_size(integrated_nodes));
}
break;
case S_POLICY_ENGINE:
election_trigger->counter = 0;
CRM_LOG_ASSERT(AM_I_DC);
if (cause == C_TIMER_POPPED) {
crm_info("Progressed to state %s after %s",
fsa_state2string(next_state), fsa_cause2string(cause));
}
if (g_hash_table_size(finalized_nodes) > 0) {
char *msg = strdup(" Confirm not received from");
crm_err("%u cluster nodes failed to confirm"
" their join.", g_hash_table_size(finalized_nodes));
g_hash_table_foreach(finalized_nodes, ghash_print_node, msg);
free(msg);
} else if (g_hash_table_size(confirmed_nodes)
== crm_active_peers()) {
crm_debug("All %u cluster nodes are"
" eligible to run resources.", crm_active_peers());
} else if (g_hash_table_size(confirmed_nodes) > crm_active_peers()) {
crm_err("We have more confirmed nodes than our membership does: %d vs. %d",
g_hash_table_size(confirmed_nodes), crm_active_peers());
register_fsa_input(C_FSA_INTERNAL, I_ELECTION, NULL);
} else if (saved_ccm_membership_id != crm_peer_seq) {
crm_info("Membership changed: %llu -> %llu - join restart",
saved_ccm_membership_id, crm_peer_seq);
register_fsa_input_before(C_FSA_INTERNAL, I_NODE_JOIN, NULL);
} else {
crm_warn("Only %u of %u cluster "
"nodes are eligible to run resources - continue %d",
g_hash_table_size(confirmed_nodes),
crm_active_peers(), g_hash_table_size(welcomed_nodes));
}
/* initialize_join(FALSE); */
break;
case S_STOPPING:
case S_TERMINATE:
/* possibly redundant */
set_bit(fsa_input_register, R_SHUTDOWN);
break;
case S_IDLE:
CRM_LOG_ASSERT(AM_I_DC);
dump_rsc_info();
if (is_set(fsa_input_register, R_SHUTDOWN)) {
crm_info("(Re)Issuing shutdown request now" " that we are the DC");
set_bit(tmp, A_SHUTDOWN_REQ);
}
if (recheck_timer->period_ms > 0) {
crm_debug("Starting %s", get_timer_desc(recheck_timer));
crm_timer_start(recheck_timer);
}
break;
default:
break;
}
if (clear_recovery_bit && next_state != S_PENDING) {
tmp &= ~A_RECOVER;
} else if (clear_recovery_bit == FALSE) {
tmp |= A_RECOVER;
}
if (tmp != actions) {
/* fsa_dump_actions(actions ^ tmp, "New actions"); */
actions = tmp;
}
return actions;
}
void
dump_rsc_info(void)
{
}
void
ghash_print_node(gpointer key, gpointer value, gpointer user_data)
{
const char *text = user_data;
const char *uname = key;
const char *value_s = value;
crm_info("%s: %s %s", text, uname, value_s);
}
diff --git a/crmd/fsa_proto.h b/crmd/fsa_proto.h
index a25856b4a6..1323c645ad 100644
--- a/crmd/fsa_proto.h
+++ b/crmd/fsa_proto.h
@@ -1,342 +1,342 @@
/*
* 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 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef XML_FSA_PROTO__H
# define XML_FSA_PROTO__H
-extern xmlNode *do_lrm_query(gboolean);
+extern xmlNode *do_lrm_query(gboolean, const char *node_name);
/* A_READCONFIG */
void
do_read_config(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);
/* A_PE_INVOKE */
void
do_pe_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);
/* A_ERROR */
void
do_error(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);
/* A_LOG */
void
do_log(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);
/* A_STARTUP */
void
do_startup(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);
/* A_CIB_START, STOP, RESTART */
void
do_cib_control(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);
/* A_HA_CONNECT */
void
do_ha_control(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);
/* A_CCM_CONNECT */
void
do_ccm_control(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);
/* A_LRM_CONNECT */
void
do_lrm_control(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state, enum crmd_fsa_input cur_input, fsa_data_t * msg_data);
/* A_PE_START, STOP, RESTART */
void
do_pe_control(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);
/* A_TE_START, STOP, RESTART */
void
do_te_control(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);
/* A_STARTED */
void
do_started(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);
/* A_MSG_ROUTE */
void
do_msg_route(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);
/* A_RECOVER */
void
do_recover(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);
/* A_ELECTION_VOTE */
void
do_election_vote(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);
/* A_ELECTION_COUNT */
void
do_election_count_vote(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);
/* A_ELECTION_CHECK */
void
do_election_check(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);
/* A_ELECT_TIMER_START, A_ELECTION_TIMEOUT */
void
do_election_timer_ctrl(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);
/* A_DC_TIMER_STOP */
void
do_timer_control(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);
# if SUPPORT_HEARTBEAT
/* A_CCM_UPDATE_CACHE */
void do_ccm_update_cache(enum crmd_fsa_cause cause, enum crmd_fsa_state cur_state,
oc_ed_t event, const oc_ev_membership_t * oc, xmlNode * xml);
# endif
/* A_CCM_EVENT */
void
do_ccm_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);
/* A_DC_TAKEOVER */
void
do_dc_takeover(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);
/* A_DC_RELEASE */
void
do_dc_release(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);
/* A_DC_JOIN_OFFER_ALL */
void
do_dc_join_offer_all(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);
/* A_DC_JOIN_OFFER_ONE */
void
do_dc_join_offer_one(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);
/* A_DC_JOIN_ACK */
void
do_dc_join_ack(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);
/* A_DC_JOIN_REQ */
void
do_dc_join_filter_offer(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);
/* A_DC_JOIN_FINALIZE */
void
do_dc_join_finalize(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);
/* A_CL_JOIN_QUERY */
/* is there a DC out there? */
void
do_cl_join_query(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);
/* A_CL_JOIN_ANNOUNCE */
void
do_cl_join_announce(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);
/* A_CL_JOIN_REQUEST */
void
do_cl_join_offer_respond(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);
/* A_CL_JOIN_RESULT */
void
do_cl_join_finalize_respond(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);
/* A_UPDATE_NODESTATUS */
void
do_update_node_status(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);
/* A_LRM_INVOKE */
void
do_lrm_invoke(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state, enum crmd_fsa_input cur_input, fsa_data_t * msg_data);
/* A_LRM_EVENT */
void
do_lrm_event(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state, enum crmd_fsa_input cur_input, fsa_data_t * msg_data);
/* A_PE_INVOKE */
void
do_pe_invoke(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);
/* A_TE_INVOKE, A_TE_CANCEL */
void
do_te_invoke(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);
/* A_TE_INVOKE */
void
do_te_copyto(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);
/* A_SHUTDOWN_REQ */
void
do_shutdown_req(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);
/* A_SHUTDOWN */
void
do_shutdown(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);
/* A_STOP */
void
do_stop(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);
/* A_EXIT_0, A_EXIT_1 */
void
do_exit(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);
void
do_dc_join_final(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);
#endif
diff --git a/crmd/join_client.c b/crmd/join_client.c
index b5fc9b148d..f1af52c3ab 100644
--- a/crmd/join_client.c
+++ b/crmd/join_client.c
@@ -1,283 +1,283 @@
/*
* 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 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <crm_internal.h>
#include <crm/crm.h>
#include <crm/cib.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crmd_fsa.h>
#include <crmd_messages.h>
int reannounce_count = 0;
void join_query_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data);
extern ha_msg_input_t *copy_ha_msg_input(ha_msg_input_t * orig);
/* A_CL_JOIN_QUERY */
/* is there a DC out there? */
void
do_cl_join_query(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)
{
xmlNode *req = create_request(CRM_OP_JOIN_ANNOUNCE, NULL, NULL,
CRM_SYSTEM_DC, CRM_SYSTEM_CRMD, NULL);
sleep(1); /* give the CCM time to propogate to the DC */
update_dc(NULL); /* Unset any existing value so that the result is not discarded */
crm_debug("Querying for a DC");
send_cluster_message(NULL, crm_msg_crmd, req, FALSE);
free_xml(req);
}
/* A_CL_JOIN_ANNOUNCE */
/* this is kind of a workaround for the fact that we may not be around
* or are otherwise unable to reply when the DC sends out A_WELCOME_ALL
*/
void
do_cl_join_announce(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)
{
/* Once we hear from the DC, we can stop the timer
*
* This timer was started either on startup or when a node
* left the CCM list
*/
/* dont announce if we're in one of these states */
if (cur_state != S_PENDING) {
crm_warn("Do not announce ourselves in state %s", fsa_state2string(cur_state));
return;
}
if (AM_I_OPERATIONAL) {
/* send as a broadcast */
xmlNode *req = create_request(CRM_OP_JOIN_ANNOUNCE, NULL, NULL,
CRM_SYSTEM_DC, CRM_SYSTEM_CRMD, NULL);
crm_debug("Announcing availability");
update_dc(NULL);
send_cluster_message(NULL, crm_msg_crmd, req, FALSE);
free_xml(req);
} else {
/* Delay announce until we have finished local startup */
crm_warn("Delaying announce until local startup is complete");
return;
}
}
static int query_call_id = 0;
/* A_CL_JOIN_REQUEST */
/* aka. accept the welcome offer */
void
do_cl_join_offer_respond(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)
{
ha_msg_input_t *input = fsa_typed_data(fsa_dt_ha_msg);
const char *welcome_from = crm_element_value(input->msg, F_CRM_HOST_FROM);
const char *join_id = crm_element_value(input->msg, F_CRM_JOIN_ID);
#if 0
if (we are sick) {
log error;
/* save the request for later? */
return;
}
#endif
crm_trace("Accepting join offer: join-%s", crm_element_value(input->msg, F_CRM_JOIN_ID));
/* we only ever want the last one */
if (query_call_id > 0) {
crm_trace("Cancelling previous join query: %d", query_call_id);
remove_cib_op_callback(query_call_id, FALSE);
query_call_id = 0;
}
if (update_dc(input->msg) == FALSE) {
crm_warn("Discarding offer from %s (expected %s)", welcome_from, fsa_our_dc);
return;
}
CRM_LOG_ASSERT(input != NULL);
query_call_id = fsa_cib_conn->cmds->query(fsa_cib_conn, NULL, NULL, cib_scope_local|cib_no_children);
fsa_register_cib_callback(query_call_id, FALSE, strdup(join_id), join_query_callback);
crm_trace("Registered join query callback: %d", query_call_id);
register_fsa_action(A_DC_TIMER_STOP);
}
void
join_query_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
{
xmlNode *local_cib = NULL;
char *join_id = user_data;
xmlNode *generation = create_xml_node(NULL, XML_CIB_TAG_GENERATION_TUPPLE);
CRM_LOG_ASSERT(join_id != NULL);
query_call_id = 0;
if (rc == pcmk_ok) {
local_cib = output;
CRM_LOG_ASSERT(safe_str_eq(crm_element_name(local_cib), XML_TAG_CIB));
}
if (local_cib != NULL) {
xmlNode *reply = NULL;
crm_debug("Respond to join offer join-%s", join_id);
crm_debug("Acknowledging %s as our DC", fsa_our_dc);
copy_in_properties(generation, local_cib);
reply = create_request(CRM_OP_JOIN_REQUEST, generation, fsa_our_dc,
CRM_SYSTEM_DC, CRM_SYSTEM_CRMD, NULL);
crm_xml_add(reply, F_CRM_JOIN_ID, join_id);
if(fsa_our_dc) {
send_cluster_message(crm_get_peer(0, fsa_our_dc), crm_msg_crmd, reply, TRUE);
} else {
crm_warn("No DC for join-%s", join_id);
send_cluster_message(NULL, crm_msg_crmd, reply, TRUE);
}
free_xml(reply);
} else {
crm_err("Could not retrieve Generation to attach to our"
" join acknowledgement: %s", pcmk_strerror(rc));
register_fsa_error_adv(C_FSA_INTERNAL, I_ERROR, NULL, NULL, __FUNCTION__);
}
free(join_id);
free_xml(generation);
}
/* A_CL_JOIN_RESULT */
/* aka. this is notification that we have (or have not) been accepted */
void
do_cl_join_finalize_respond(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)
{
xmlNode *tmp1 = NULL;
gboolean was_nack = TRUE;
static gboolean first_join = TRUE;
ha_msg_input_t *input = fsa_typed_data(fsa_dt_ha_msg);
int join_id = -1;
const char *op = crm_element_value(input->msg, F_CRM_TASK);
const char *ack_nack = crm_element_value(input->msg, CRM_OP_JOIN_ACKNAK);
const char *welcome_from = crm_element_value(input->msg, F_CRM_HOST_FROM);
if (safe_str_neq(op, CRM_OP_JOIN_ACKNAK)) {
crm_trace("Ignoring op=%s message", op);
return;
}
/* calculate if it was an ack or a nack */
if (crm_is_true(ack_nack)) {
was_nack = FALSE;
}
crm_element_value_int(input->msg, F_CRM_JOIN_ID, &join_id);
if (was_nack) {
crm_err("Join (join-%d) with leader %s failed (NACK'd): Shutting down",
join_id, welcome_from);
register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
return;
}
if (AM_I_DC == FALSE && safe_str_eq(welcome_from, fsa_our_uname)) {
crm_warn("Discarding our own welcome - we're no longer the DC");
return;
}
if (update_dc(input->msg) == FALSE) {
crm_warn("Discarding %s from %s (expected %s)", op, welcome_from, fsa_our_dc);
return;
}
/* send our status section to the DC */
crm_debug("Confirming join join-%d: %s", join_id, crm_element_value(input->msg, F_CRM_TASK));
- tmp1 = do_lrm_query(TRUE);
+ tmp1 = do_lrm_query(TRUE, fsa_our_uname);
if (tmp1 != NULL) {
xmlNode *reply = create_request(CRM_OP_JOIN_CONFIRM, tmp1, fsa_our_dc,
CRM_SYSTEM_DC, CRM_SYSTEM_CRMD, NULL);
crm_xml_add_int(reply, F_CRM_JOIN_ID, join_id);
crm_debug("join-%d: Join complete."
" Sending local LRM status to %s", join_id, fsa_our_dc);
if (first_join) {
first_join = FALSE;
/*
* Clear any previous transient node attribute and lrm operations
*
* Corosync has a nasty habit of not being able to tell if a
* node is returning or didn't leave in the first place.
* This confuses Pacemaker because it never gets a "node up"
* event which is normally used to clean up the status section.
*
* Do not remove the resources though, they'll be cleaned up in
* do_dc_join_ack(). Removing them here creates a race
* condition if the crmd is being recovered.
* Instead of a list of active resources from the lrmd
* we may end up with a blank status section.
* If we are _NOT_ lucky, we will probe for the "wrong" instance
* of anonymous clones and end up with multiple active
* instances on the machine.
*/
erase_status_tag(fsa_our_uname, XML_TAG_TRANSIENT_NODEATTRS, 0);
/* Just in case attrd was still around too */
if (is_not_set(fsa_input_register, R_SHUTDOWN)) {
update_attrd(fsa_our_uname, "terminate", NULL, NULL);
update_attrd(fsa_our_uname, XML_CIB_ATTR_SHUTDOWN, NULL, NULL);
}
}
send_cluster_message(crm_get_peer(0, fsa_our_dc), crm_msg_crmd, reply, TRUE);
free_xml(reply);
if (AM_I_DC == FALSE) {
register_fsa_input_adv(cause, I_NOT_DC, NULL, A_NOTHING, TRUE, __FUNCTION__);
update_attrd(NULL, NULL, NULL, NULL);
}
free_xml(tmp1);
} else {
crm_err("Could not send our LRM state to the DC");
register_fsa_error(C_FSA_INTERNAL, I_FAIL, NULL);
}
}
diff --git a/crmd/lrm.c b/crmd/lrm.c
index a34c76ba8d..743a0cc9bd 100644
--- a/crmd/lrm.c
+++ b/crmd/lrm.c
@@ -1,2000 +1,1997 @@
/*
* 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 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <crm_internal.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <crm/crm.h>
#include <crm/services.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crmd.h>
#include <crmd_fsa.h>
#include <crmd_messages.h>
#include <crmd_callbacks.h>
#include <crmd_lrm.h>
#define START_DELAY_THRESHOLD 5 * 60 * 1000
-
-typedef struct resource_history_s {
- char *id;
- lrmd_rsc_info_t rsc;
- lrmd_event_data_t *last;
- lrmd_event_data_t *failed;
- GList *recurring_op_list;
-
- /* Resources must be stopped using the same
- * parameters they were started with. This hashtable
- * holds the parameters that should be used for the next stop
- * cmd on this resource. */
- GHashTable *stop_params;
-} rsc_history_t;
-
-struct recurring_op_s {
- char *rsc_id;
- char *op_type;
- char *op_key;
- int call_id;
- int interval;
- gboolean remove;
- gboolean cancelled;
-};
-
-struct pending_deletion_op_s {
- char *rsc;
- ha_msg_input_t *input;
-};
+#define MAX_LRM_REG_FAILS 30
struct delete_event_s {
int rc;
const char *rsc;
+ lrm_state_t *lrm_state;
};
-GHashTable *resource_history = NULL;
-GHashTable *pending_ops = NULL;
-GHashTable *deletion_ops = NULL;
-
-int num_lrm_register_fails = 0;
-int max_lrm_register_fails = 30;
-gboolean process_lrm_event(lrmd_event_data_t * op);
-gboolean is_rsc_active(const char *rsc_id);
-gboolean build_active_RAs(xmlNode * rsc_list);
+gboolean process_lrm_event(lrm_state_t *lrm_state, lrmd_event_data_t * op);
+static gboolean is_rsc_active(lrm_state_t *lrm_state, const char *rsc_id);
+static gboolean build_active_RAs(lrm_state_t *lrm_state, xmlNode * rsc_list);
static gboolean stop_recurring_actions(gpointer key, gpointer value, gpointer user_data);
-static int delete_rsc_status(const char *rsc_id, int call_options, const char *user_name);
+static int delete_rsc_status(lrm_state_t *lrm_state, const char *rsc_id, int call_options, const char *user_name);
-static lrmd_event_data_t *construct_op(xmlNode * rsc_op, const char *rsc_id, const char *operation);
-void do_lrm_rsc_op(lrmd_rsc_info_t * rsc, const char *operation, xmlNode * msg, xmlNode * request);
+static lrmd_event_data_t *construct_op(lrm_state_t *lrm_state, xmlNode * rsc_op, const char *rsc_id, const char *operation);
+static void do_lrm_rsc_op(lrm_state_t *lrm_state, lrmd_rsc_info_t * rsc, const char *operation, xmlNode * msg, xmlNode * request);
void send_direct_ack(const char *to_host, const char *to_sys,
lrmd_rsc_info_t * rsc, lrmd_event_data_t * op, const char *rsc_id);
+static gboolean lrm_state_verify_stopped(lrm_state_t *lrm_state, enum crmd_fsa_state cur_state, int log_level);
+
static void
lrm_connection_destroy(void)
{
if (is_set(fsa_input_register, R_LRM_CONNECTED)) {
crm_crit("LRM Connection failed");
register_fsa_input(C_FSA_INTERNAL, I_ERROR, NULL);
clear_bit(fsa_input_register, R_LRM_CONNECTED);
} else {
crm_info("LRM Connection disconnected");
}
}
-static void
-free_deletion_op(gpointer value)
-{
- struct pending_deletion_op_s *op = value;
-
- free(op->rsc);
- delete_ha_msg_input(op->input);
- free(op);
-}
-
-static void
-free_recurring_op(gpointer value)
-{
- struct recurring_op_s *op = (struct recurring_op_s *)value;
-
- free(op->rsc_id);
- free(op->op_type);
- free(op->op_key);
- free(op);
-}
-
static char *
make_stop_id(const char *rsc, int call_id)
{
char *op_id = NULL;
op_id = calloc(1, strlen(rsc) + 34);
if (op_id != NULL) {
snprintf(op_id, strlen(rsc) + 34, "%s:%d", rsc, call_id);
}
return op_id;
}
static void
copy_instance_keys(gpointer key, gpointer value, gpointer user_data)
{
if (strstr(key, CRM_META "_") == NULL) {
g_hash_table_replace(user_data, strdup((const char *)key), strdup((const char *)value));
}
}
static void
copy_meta_keys(gpointer key, gpointer value, gpointer user_data)
{
if (strstr(key, CRM_META "_") != NULL) {
g_hash_table_replace(user_data, strdup((const char *)key), strdup((const char *)value));
}
}
static void
-history_cache_destroy(gpointer data)
-{
- rsc_history_t *entry = data;
-
- if (entry->stop_params) {
- g_hash_table_destroy(entry->stop_params);
- }
-
- free(entry->rsc.type);
- free(entry->rsc.class);
- free(entry->rsc.provider);
-
- lrmd_free_event(entry->failed);
- lrmd_free_event(entry->last);
- free(entry->id);
- free(entry);
-}
-
-static void
-update_history_cache(lrmd_rsc_info_t * rsc, lrmd_event_data_t * op)
+update_history_cache(lrm_state_t *lrm_state, lrmd_rsc_info_t * rsc, lrmd_event_data_t * op)
{
int target_rc = 0;
rsc_history_t *entry = NULL;
if (op->rsc_deleted) {
crm_debug("Purged history for '%s' after %s", op->rsc_id, op->op_type);
- delete_rsc_status(op->rsc_id, cib_quorum_override, NULL);
+ delete_rsc_status(lrm_state, op->rsc_id, cib_quorum_override, NULL);
return;
}
if (safe_str_eq(op->op_type, RSC_NOTIFY)) {
return;
}
crm_debug("Updating history for '%s' with %s op", op->rsc_id, op->op_type);
- entry = g_hash_table_lookup(resource_history, op->rsc_id);
+ entry = g_hash_table_lookup(lrm_state->resource_history, op->rsc_id);
if (entry == NULL && rsc) {
entry = calloc(1, sizeof(rsc_history_t));
entry->id = strdup(op->rsc_id);
- g_hash_table_insert(resource_history, entry->id, entry);
+ g_hash_table_insert(lrm_state->resource_history, entry->id, entry);
entry->rsc.id = entry->id;
entry->rsc.type = strdup(rsc->type);
entry->rsc.class = strdup(rsc->class);
if (rsc->provider) {
entry->rsc.provider = strdup(rsc->provider);
} else {
entry->rsc.provider = NULL;
}
} else if (entry == NULL) {
crm_info("Resource %s no longer exists, not updating cache", op->rsc_id);
return;
}
target_rc = rsc_op_expected_rc(op);
if (op->op_status == PCMK_LRM_OP_CANCELLED) {
if (op->interval > 0) {
GList *gIter, *gIterNext;
crm_trace("Removing cancelled recurring op: %s_%s_%d", op->rsc_id, op->op_type, op->interval);
for (gIter = entry->recurring_op_list; gIter != NULL; gIter = gIterNext) {
lrmd_event_data_t *existing = gIter->data;
gIterNext = gIter->next;
if (safe_str_eq(op->rsc_id, existing->rsc_id)
&& safe_str_eq(op->op_type, existing->op_type)
&& op->interval == existing->interval) {
lrmd_free_event(existing);
entry->recurring_op_list = g_list_delete_link(entry->recurring_op_list, gIter);
}
}
return;
} else {
crm_trace("Skipping %s_%s_%d rc=%d, status=%d", op->rsc_id, op->op_type, op->interval,
op->rc, op->op_status);
}
} else if (did_rsc_op_fail(op, target_rc)) {
/* We must store failed monitors here
* - otherwise the block below will cause them to be forgetten them when a stop happens
*/
if (entry->failed) {
lrmd_free_event(entry->failed);
}
entry->failed = lrmd_copy_event(op);
} else if (op->interval == 0) {
if (entry->last) {
lrmd_free_event(entry->last);
}
entry->last = lrmd_copy_event(op);
if (op->params &&
(safe_str_eq(CRMD_ACTION_START, op->op_type) ||
safe_str_eq(CRMD_ACTION_STATUS, op->op_type))) {
if (entry->stop_params) {
g_hash_table_destroy(entry->stop_params);
}
entry->stop_params = g_hash_table_new_full(crm_str_hash,
g_str_equal, g_hash_destroy_str, g_hash_destroy_str);
g_hash_table_foreach(op->params, copy_instance_keys, entry->stop_params);
}
}
if (op->interval > 0) {
crm_trace("Adding recurring op: %s_%s_%d", op->rsc_id, op->op_type, op->interval);
entry->recurring_op_list = g_list_prepend(entry->recurring_op_list, lrmd_copy_event(op));
} else if (entry->recurring_op_list && safe_str_eq(op->op_type, RSC_STATUS) == FALSE) {
GList *gIter = entry->recurring_op_list;
crm_trace("Dropping %d recurring ops because of: %s_%s_%d",
g_list_length(gIter), op->rsc_id, op->op_type, op->interval);
for (; gIter != NULL; gIter = gIter->next) {
lrmd_free_event(gIter->data);
}
g_list_free(entry->recurring_op_list);
entry->recurring_op_list = NULL;
}
}
+void
+lrm_op_callback(lrmd_event_data_t * op)
+{
+ const char *nodename = NULL;
+ lrm_state_t *lrm_state = NULL;
+
+ CRM_CHECK(op != NULL, return);
+
+ /* determine if this is a callback for a remote or local connection */
+ nodename = op->remote_nodename ? op->remote_nodename : fsa_our_uname;
+ lrm_state = lrm_state_find(nodename);
+
+ CRM_ASSERT(lrm_state != NULL);
+
+ process_lrm_event(lrm_state, op);
+}
+
/* A_LRM_CONNECT */
void
do_lrm_control(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input, fsa_data_t * msg_data)
{
- if (fsa_lrm_conn == NULL) {
+ /* This only pertains to local lrmd connections. Remote connections are handled as
+ * resources within the pengine. Connecting and disconnecting from remote lrmd instances
+ * handled differently than the local. */
+
+ lrm_state_t *lrm_state = NULL;
+
+ lrm_state = lrm_state_find_or_create_local(fsa_our_uname);
+ if (lrm_state == NULL) {
register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
return;
}
if (action & A_LRM_DISCONNECT) {
- if (verify_stopped(cur_state, LOG_INFO) == FALSE) {
+ if (lrm_state_verify_stopped(lrm_state, cur_state, LOG_INFO) == FALSE) {
if(action == A_LRM_DISCONNECT) {
crmd_fsa_stall(FALSE);
return;
}
}
- if (fsa_lrm_conn) {
- clear_bit(fsa_input_register, R_LRM_CONNECTED);
- fsa_lrm_conn->cmds->disconnect(fsa_lrm_conn);
- }
-
+ clear_bit(fsa_input_register, R_LRM_CONNECTED);
+ lrm_state->conn->cmds->disconnect(lrm_state->conn);
+ lrm_state_reset_tables(lrm_state);
crm_notice("Disconnected from the LRM");
- if(resource_history) {
- g_hash_table_destroy(resource_history);
- resource_history = NULL;
- }
- if(deletion_ops) {
- g_hash_table_destroy(deletion_ops);
- deletion_ops = NULL;
- }
- if(pending_ops) {
- g_hash_table_destroy(pending_ops);
- pending_ops = NULL;
- }
}
if (action & A_LRM_CONNECT) {
int ret = pcmk_ok;
- deletion_ops = g_hash_table_new_full(crm_str_hash, g_str_equal,
- g_hash_destroy_str, free_deletion_op);
-
- pending_ops = g_hash_table_new_full(crm_str_hash, g_str_equal,
- g_hash_destroy_str, free_recurring_op);
-
- resource_history = g_hash_table_new_full(crm_str_hash, g_str_equal,
- NULL, history_cache_destroy);
-
crm_debug("Connecting to the LRM");
- ret = fsa_lrm_conn->cmds->connect(fsa_lrm_conn, CRM_SYSTEM_CRMD, NULL);
+ ret = lrm_state->conn->cmds->connect(lrm_state->conn, CRM_SYSTEM_CRMD, NULL);
if (ret != pcmk_ok) {
- if (++num_lrm_register_fails < max_lrm_register_fails) {
+ if (++lrm_state->num_lrm_register_fails < MAX_LRM_REG_FAILS) {
crm_warn("Failed to sign on to the LRM %d"
- " (%d max) times", num_lrm_register_fails, max_lrm_register_fails);
+ " (%d max) times", lrm_state->num_lrm_register_fails, MAX_LRM_REG_FAILS);
crm_timer_start(wait_timer);
crmd_fsa_stall(FALSE);
return;
}
}
- if (ret == pcmk_ok) {
- crm_trace("LRM: set_lrm_callback...");
- fsa_lrm_conn->cmds->set_callback(fsa_lrm_conn, lrm_op_callback);
- }
-
if (ret != pcmk_ok) {
- crm_err("Failed to sign on to the LRM %d" " (max) times", num_lrm_register_fails);
+ crm_err("Failed to sign on to the LRM %d" " (max) times", lrm_state->num_lrm_register_fails);
register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
return;
}
set_bit(fsa_input_register, R_LRM_CONNECTED);
crm_debug("LRM connection established");
}
if (action & ~(A_LRM_CONNECT | A_LRM_DISCONNECT)) {
crm_err("Unexpected action %s in %s", fsa_action2string(action), __FUNCTION__);
}
}
static void
ghash_print_pending(gpointer key, gpointer value, gpointer user_data)
{
const char *stop_id = key;
int *log_level = user_data;
struct recurring_op_s *pending = value;
do_crm_log(*log_level, "Pending action: %s (%s)", stop_id, pending->op_key);
}
static void
ghash_print_pending_for_rsc(gpointer key, gpointer value, gpointer user_data)
{
const char *stop_id = key;
char *rsc = user_data;
struct recurring_op_s *pending = value;
if (safe_str_eq(rsc, pending->rsc_id)) {
crm_notice("%sction %s (%s) incomplete at shutdown",
pending->interval == 0 ? "A" : "Recurring a", stop_id, pending->op_key);
}
}
static void
ghash_count_pending(gpointer key, gpointer value, gpointer user_data)
{
int *counter = user_data;
struct recurring_op_s *pending = value;
if (pending->interval > 0) {
/* Ignore recurring actions in the shutdown calculations */
return;
}
(*counter)++;
}
-gboolean
-verify_stopped(enum crmd_fsa_state cur_state, int log_level)
+static gboolean
+lrm_state_verify_stopped(lrm_state_t *lrm_state, enum crmd_fsa_state cur_state, int log_level)
{
int counter = 0;
gboolean rc = TRUE;
const char *when = "lrm disconnect";
GHashTableIter gIter;
rsc_history_t *entry = NULL;
crm_debug("Checking for active resources before exit");
if (cur_state == S_TERMINATE) {
log_level = LOG_ERR;
when = "shutdown";
} else if (is_set(fsa_input_register, R_SHUTDOWN)) {
when = "shutdown... waiting";
}
- if (pending_ops) {
+ if (lrm_state->pending_ops) {
+ /* TODO check if the state connection is connected, not if fsa bit is set */
if (is_set(fsa_input_register, R_LRM_CONNECTED)) {
/* Only log/complain about non-recurring actions */
- g_hash_table_foreach_remove(pending_ops, stop_recurring_actions, NULL);
+ g_hash_table_foreach_remove(lrm_state->pending_ops, stop_recurring_actions, NULL);
}
- g_hash_table_foreach(pending_ops, ghash_count_pending, &counter);
+ g_hash_table_foreach(lrm_state->pending_ops, ghash_count_pending, &counter);
}
if (counter > 0) {
do_crm_log(log_level, "%d pending LRM operations at %s%s", counter, when);
if (cur_state == S_TERMINATE || !is_set(fsa_input_register, R_SENT_RSC_STOP)) {
- g_hash_table_foreach(pending_ops, ghash_print_pending, &log_level);
+ g_hash_table_foreach(lrm_state->pending_ops, ghash_print_pending, &log_level);
} else {
rc = FALSE;
}
- goto bail;
+ return rc;
}
- if (resource_history == NULL) {
- goto bail;
+ if (lrm_state->resource_history == NULL) {
+ return rc;
}
counter = 0;
- g_hash_table_iter_init(&gIter, resource_history);
+ g_hash_table_iter_init(&gIter, lrm_state->resource_history);
while (g_hash_table_iter_next(&gIter, NULL, (void **)&entry)) {
- if (is_rsc_active(entry->id) == FALSE) {
+ if (is_rsc_active(lrm_state, entry->id) == FALSE) {
continue;
}
counter++;
crm_trace("Found %s active", entry->id);
- g_hash_table_foreach(pending_ops, ghash_print_pending_for_rsc, entry->id);
+ if (lrm_state->pending_ops) {
+ g_hash_table_foreach(lrm_state->pending_ops, ghash_print_pending_for_rsc, entry->id);
+ }
}
if(counter) {
crm_err("%d resources were active at %s.", counter, when);
}
-
- bail:
- set_bit(fsa_input_register, R_SENT_RSC_STOP);
+
return rc;
}
static char *
get_rsc_metadata(const char *type, const char *class, const char *provider)
{
char *metadata = NULL;
+ /* Always use a local connection for this operation */
+ lrm_state_t *lrm_state = lrm_state_find(fsa_our_uname);
CRM_CHECK(type != NULL, return NULL);
CRM_CHECK(class != NULL, return NULL);
+ CRM_CHECK(lrm_state != NULL, return NULL);
+
if (provider == NULL) {
provider = "heartbeat";
}
crm_trace("Retreiving metadata for %s::%s:%s", type, class, provider);
- fsa_lrm_conn->cmds->get_metadata(fsa_lrm_conn, class, provider, type, &metadata, 0);
+ lrm_state->conn->cmds->get_metadata(lrm_state->conn, class, provider, type, &metadata, 0);
if (metadata) {
/* copy the metadata because the LRM likes using
* g_alloc instead of cl_malloc
*/
char *m_copy = strdup(metadata);
g_free(metadata);
metadata = m_copy;
} else {
crm_warn("No metadata found for %s::%s:%s", type, class, provider);
}
return metadata;
}
typedef struct reload_data_s {
char *key;
char *metadata;
time_t last_query;
gboolean can_reload;
GListPtr restart_list;
} reload_data_t;
static void
g_hash_destroy_reload(gpointer data)
{
reload_data_t *reload = data;
free(reload->key);
free(reload->metadata);
g_list_free_full(reload->restart_list, free);
free(reload);
}
GHashTable *reload_hash = NULL;
static GListPtr
get_rsc_restart_list(lrmd_rsc_info_t * rsc, lrmd_event_data_t * op)
{
int len = 0;
char *key = NULL;
char *copy = NULL;
const char *value = NULL;
const char *provider = NULL;
xmlNode *param = NULL;
xmlNode *params = NULL;
xmlNode *actions = NULL;
xmlNode *metadata = NULL;
time_t now = time(NULL);
reload_data_t *reload = NULL;
if (reload_hash == NULL) {
reload_hash = g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, g_hash_destroy_reload);
}
provider = rsc->provider;
if (provider == NULL) {
provider = "heartbeat";
}
len = strlen(rsc->type) + strlen(rsc->class) + strlen(provider) + 4;
/* coverity[returned_null] Ignore */
key = malloc( len);
snprintf(key, len, "%s::%s:%s", rsc->type, rsc->class, provider);
reload = g_hash_table_lookup(reload_hash, key);
if (reload && ((now - 9) > reload->last_query)
&& safe_str_eq(op->op_type, RSC_START)) {
reload = NULL; /* re-query */
}
if (reload == NULL) {
xmlNode *action = NULL;
reload = calloc(1, sizeof(reload_data_t));
g_hash_table_replace(reload_hash, key, reload);
reload->last_query = now;
reload->key = key;
key = NULL;
reload->metadata = get_rsc_metadata(rsc->type, rsc->class, provider);
metadata = string2xml(reload->metadata);
if (metadata == NULL) {
crm_err("Metadata for %s::%s:%s is not valid XML",
rsc->provider, rsc->class, rsc->type);
goto cleanup;
}
actions = find_xml_node(metadata, "actions", TRUE);
for (action = __xml_first_child(actions); action != NULL; action = __xml_next(action)) {
if (crm_str_eq((const char *)action->name, "action", TRUE)) {
value = crm_element_value(action, "name");
if (safe_str_eq("reload", value)) {
reload->can_reload = TRUE;
break;
}
}
}
if (reload->can_reload == FALSE) {
goto cleanup;
}
params = find_xml_node(metadata, "parameters", TRUE);
for (param = __xml_first_child(params); param != NULL; param = __xml_next(param)) {
if (crm_str_eq((const char *)param->name, "parameter", TRUE)) {
value = crm_element_value(param, "unique");
if (crm_is_true(value)) {
value = crm_element_value(param, "name");
if (value == NULL) {
crm_err("%s: NULL param", key);
continue;
}
crm_debug("Attr %s is not reloadable", value);
copy = strdup(value);
CRM_CHECK(copy != NULL, continue);
reload->restart_list = g_list_append(reload->restart_list, copy);
}
}
}
}
cleanup:
free(key);
free_xml(metadata);
return reload->restart_list;
}
static void
append_restart_list(lrmd_rsc_info_t * rsc, lrmd_event_data_t * op, xmlNode * update, const char *version)
{
int len = 0;
char *list = NULL;
char *digest = NULL;
const char *value = NULL;
xmlNode *restart = NULL;
GListPtr restart_list = NULL;
GListPtr lpc = NULL;
if (op->interval > 0) {
/* monitors are not reloadable */
return;
} else if (op->params == NULL) {
crm_debug("%s has no parameters", ID(update));
return;
} else if (rsc == NULL) {
return;
} else if (crm_str_eq(CRMD_ACTION_STOP, op->op_type, TRUE)) {
/* Stopped resources don't need to be reloaded */
return;
} else if (compare_version("1.0.8", version) > 0) {
/* Caller version does not support reloads */
return;
}
restart_list = get_rsc_restart_list(rsc, op);
if (restart_list == NULL) {
/* Resource does not support reloads */
return;
}
restart = create_xml_node(NULL, XML_TAG_PARAMS);
for (lpc = restart_list; lpc != NULL; lpc = lpc->next) {
const char *param = (const char *)lpc->data;
int start = len;
CRM_CHECK(param != NULL, continue);
value = g_hash_table_lookup(op->params, param);
if (value != NULL) {
crm_xml_add(restart, param, value);
}
len += strlen(param) + 2;
list = realloc(list, len + 1);
sprintf(list + start, " %s ", param);
}
digest = calculate_operation_digest(restart, version);
crm_xml_add(update, XML_LRM_ATTR_OP_RESTART, list);
crm_xml_add(update, XML_LRM_ATTR_RESTART_DIGEST, digest);
crm_trace("%s: %s, %s", rsc->id, digest, list);
crm_log_xml_trace(restart, "restart digest source");
free_xml(restart);
free(digest);
free(list);
}
static gboolean
build_operation_update(xmlNode * parent, lrmd_rsc_info_t * rsc, lrmd_event_data_t * op, const char *src)
{
int target_rc = 0;
xmlNode *xml_op = NULL;
const char *caller_version = CRM_FEATURE_SET;
if (op == NULL) {
return FALSE;
} else if (AM_I_DC) {
} else if (fsa_our_dc_version != NULL) {
caller_version = fsa_our_dc_version;
} else if (op->params == NULL) {
caller_version = fsa_our_dc_version;
} else {
/* there is a small risk in formerly mixed clusters that
* it will be sub-optimal.
* however with our upgrade policy, the update we send
* should still be completely supported anyway
*/
caller_version = g_hash_table_lookup(op->params, XML_ATTR_CRM_VERSION);
crm_debug("Falling back to operation originator version: %s", caller_version);
}
target_rc = rsc_op_expected_rc(op);
xml_op = create_operation_update(parent, op, caller_version, target_rc, src, LOG_DEBUG);
if (xml_op) {
append_restart_list(rsc, op, xml_op, caller_version);
}
return TRUE;
}
-gboolean
-is_rsc_active(const char *rsc_id)
+static gboolean
+is_rsc_active(lrm_state_t *lrm_state, const char *rsc_id)
{
rsc_history_t *entry = NULL;
- entry = g_hash_table_lookup(resource_history, rsc_id);
+ entry = g_hash_table_lookup(lrm_state->resource_history, rsc_id);
if (entry == NULL || entry->last == NULL) {
return FALSE;
}
crm_trace("Processing %s: %s.%d=%d",
rsc_id, entry->last->op_type, entry->last->interval, entry->last->rc);
if (entry->last->rc == PCMK_EXECRA_OK && safe_str_eq(entry->last->op_type, CRMD_ACTION_STOP)) {
return FALSE;
} else if (entry->last->rc == PCMK_EXECRA_OK
&& safe_str_eq(entry->last->op_type, CRMD_ACTION_MIGRATE)) {
/* a stricter check is too complex...
* leave that to the PE
*/
return FALSE;
} else if (entry->last->rc == PCMK_EXECRA_NOT_RUNNING) {
return FALSE;
} else if (entry->last->interval == 0 && entry->last->rc == PCMK_EXECRA_NOT_CONFIGURED) {
/* Badly configured resources can't be reliably stopped */
return FALSE;
}
return TRUE;
}
-gboolean
-build_active_RAs(xmlNode * rsc_list)
+static gboolean
+build_active_RAs(lrm_state_t *lrm_state, xmlNode * rsc_list)
{
GHashTableIter iter;
rsc_history_t *entry = NULL;
- g_hash_table_iter_init(&iter, resource_history);
+ g_hash_table_iter_init(&iter, lrm_state->resource_history);
while (g_hash_table_iter_next(&iter, NULL, (void **)&entry)) {
GList *gIter = NULL;
xmlNode *xml_rsc = create_xml_node(rsc_list, XML_LRM_TAG_RESOURCE);
crm_xml_add(xml_rsc, XML_ATTR_ID, entry->id);
crm_xml_add(xml_rsc, XML_ATTR_TYPE, entry->rsc.type);
crm_xml_add(xml_rsc, XML_AGENT_ATTR_CLASS, entry->rsc.class);
crm_xml_add(xml_rsc, XML_AGENT_ATTR_PROVIDER, entry->rsc.provider);
build_operation_update(xml_rsc, &(entry->rsc), entry->last, __FUNCTION__);
build_operation_update(xml_rsc, &(entry->rsc), entry->failed, __FUNCTION__);
for (gIter = entry->recurring_op_list; gIter != NULL; gIter = gIter->next) {
build_operation_update(xml_rsc, &(entry->rsc), gIter->data, __FUNCTION__);
}
}
return FALSE;
}
-xmlNode *
-do_lrm_query(gboolean is_replace)
+static xmlNode *
+do_lrm_query_internal(lrm_state_t *lrm_state, gboolean is_replace)
{
xmlNode *xml_result = NULL;
xmlNode *xml_state = NULL;
xmlNode *xml_data = NULL;
xmlNode *rsc_list = NULL;
- crm_node_t *peer = crm_get_peer(0, fsa_our_uname);
+ crm_node_t *peer = crm_get_peer(0, lrm_state->node_name);
xml_state = do_update_node_cib(peer, node_update_cluster|node_update_peer, NULL, __FUNCTION__);
/* The next two lines shouldn't be necessary for newer DCs */
crm_xml_add(xml_state, XML_NODE_JOIN_STATE, CRMD_JOINSTATE_MEMBER);
crm_xml_add(xml_state, XML_NODE_EXPECTED, CRMD_JOINSTATE_MEMBER);
xml_data = create_xml_node(xml_state, XML_CIB_TAG_LRM);
crm_xml_add(xml_data, XML_ATTR_ID, fsa_our_uuid);
rsc_list = create_xml_node(xml_data, XML_LRM_TAG_RESOURCES);
/* Build a list of active (not always running) resources */
- build_active_RAs(rsc_list);
+ build_active_RAs(lrm_state, rsc_list);
xml_result = create_cib_fragment(xml_state, XML_CIB_TAG_STATUS);
crm_log_xml_trace(xml_state, "Current state of the LRM");
free_xml(xml_state);
return xml_result;
}
+xmlNode *
+do_lrm_query(gboolean is_replace, const char *node_name)
+{
+ lrm_state_t *lrm_state = lrm_state_find(node_name);
+ if (!lrm_state) {
+ crm_err("Could not query lrm state for lrmd node %s", node_name);
+ return NULL;
+ }
+ return do_lrm_query_internal(lrm_state, is_replace);
+}
+
static void
-notify_deleted(ha_msg_input_t * input, const char *rsc_id, int rc)
+notify_deleted(lrm_state_t *lrm_state, ha_msg_input_t * input, const char *rsc_id, int rc)
{
lrmd_event_data_t *op = NULL;
const char *from_sys = crm_element_value(input->msg, F_CRM_SYS_FROM);
const char *from_host = crm_element_value(input->msg, F_CRM_HOST_FROM);
crm_info("Notifying %s on %s that %s was%s deleted",
from_sys, from_host, rsc_id, rc == pcmk_ok ? "" : " not");
- op = construct_op(input->xml, rsc_id, CRMD_ACTION_DELETE);
+ op = construct_op(lrm_state, input->xml, rsc_id, CRMD_ACTION_DELETE);
CRM_ASSERT(op != NULL);
if (rc == pcmk_ok) {
op->op_status = PCMK_LRM_OP_DONE;
op->rc = PCMK_EXECRA_OK;
} else {
op->op_status = PCMK_LRM_OP_ERROR;
op->rc = PCMK_EXECRA_UNKNOWN_ERROR;
}
send_direct_ack(from_host, from_sys, NULL, op, rsc_id);
lrmd_free_event(op);
if (safe_str_neq(from_sys, CRM_SYSTEM_TENGINE)) {
/* this isn't expected - trigger a new transition */
time_t now = time(NULL);
char *now_s = crm_itoa(now);
crm_debug("Triggering a refresh after %s deleted %s from the LRM", from_sys, rsc_id);
update_attr_delegate(
fsa_cib_conn, cib_none, XML_CIB_TAG_CRMCONFIG, NULL, NULL, NULL, NULL,
"last-lrm-refresh", now_s, FALSE, NULL);
free(now_s);
}
}
static gboolean
lrm_remove_deleted_rsc(gpointer key, gpointer value, gpointer user_data)
{
struct delete_event_s *event = user_data;
struct pending_deletion_op_s *op = value;
if (safe_str_eq(event->rsc, op->rsc)) {
- notify_deleted(op->input, event->rsc, event->rc);
+ notify_deleted(event->lrm_state, op->input, event->rsc, event->rc);
return TRUE;
}
return FALSE;
}
static gboolean
lrm_remove_deleted_op(gpointer key, gpointer value, gpointer user_data)
{
const char *rsc = user_data;
struct recurring_op_s *pending = value;
if (safe_str_eq(rsc, pending->rsc_id)) {
crm_info("Removing op %s:%d for deleted resource %s",
pending->op_key, pending->call_id, rsc);
return TRUE;
}
return FALSE;
}
/*
* Remove the rsc from the CIB
*
* Avoids refreshing the entire LRM section of this host
*/
#define rsc_template "//"XML_CIB_TAG_STATE"[@uname='%s']//"XML_LRM_TAG_RESOURCE"[@id='%s']"
static int
-delete_rsc_status(const char *rsc_id, int call_options, const char *user_name)
+delete_rsc_status(lrm_state_t *lrm_state, const char *rsc_id, int call_options, const char *user_name)
{
char *rsc_xpath = NULL;
int max = 0;
int rc = pcmk_ok;
CRM_CHECK(rsc_id != NULL, return -ENXIO);
- max = strlen(rsc_template) + strlen(rsc_id) + strlen(fsa_our_uname) + 1;
+ max = strlen(rsc_template) + strlen(rsc_id) + strlen(lrm_state->node_name) + 1;
rsc_xpath = calloc(1, max);
- snprintf(rsc_xpath, max, rsc_template, fsa_our_uname, rsc_id);
+ snprintf(rsc_xpath, max, rsc_template, lrm_state->node_name, rsc_id);
rc = cib_internal_op(fsa_cib_conn, CIB_OP_DELETE, NULL, rsc_xpath,
NULL, NULL, call_options | cib_xpath, user_name);
free(rsc_xpath);
return rc;
}
static void
-delete_rsc_entry(ha_msg_input_t * input, const char *rsc_id, GHashTableIter *rsc_gIter, int rc, const char *user_name)
+delete_rsc_entry(lrm_state_t *lrm_state, ha_msg_input_t * input, const char *rsc_id, GHashTableIter *rsc_gIter, int rc, const char *user_name)
{
struct delete_event_s event;
CRM_CHECK(rsc_id != NULL, return);
if (rc == pcmk_ok) {
char *rsc_id_copy = strdup(rsc_id);
if (rsc_gIter)
g_hash_table_iter_remove(rsc_gIter);
else
- g_hash_table_remove(resource_history, rsc_id_copy);
+ g_hash_table_remove(lrm_state->resource_history, rsc_id_copy);
crm_debug("sync: Sending delete op for %s", rsc_id_copy);
- delete_rsc_status(rsc_id_copy, cib_quorum_override, user_name);
+ delete_rsc_status(lrm_state, rsc_id_copy, cib_quorum_override, user_name);
- g_hash_table_foreach_remove(pending_ops, lrm_remove_deleted_op, rsc_id_copy);
+ g_hash_table_foreach_remove(lrm_state->pending_ops, lrm_remove_deleted_op, rsc_id_copy);
free(rsc_id_copy);
}
if (input) {
- notify_deleted(input, rsc_id, rc);
+ notify_deleted(lrm_state, input, rsc_id, rc);
}
event.rc = rc;
event.rsc = rsc_id;
- g_hash_table_foreach_remove(deletion_ops, lrm_remove_deleted_rsc, &event);
+ event.lrm_state = lrm_state;
+ g_hash_table_foreach_remove(lrm_state->deletion_ops, lrm_remove_deleted_rsc, &event);
}
/*
* Remove the op from the CIB
*
* Avoids refreshing the entire LRM section of this host
*/
#define op_template "//"XML_CIB_TAG_STATE"[@uname='%s']//"XML_LRM_TAG_RESOURCE"[@id='%s']/"XML_LRM_TAG_RSC_OP"[@id='%s']"
#define op_call_template "//"XML_CIB_TAG_STATE"[@uname='%s']//"XML_LRM_TAG_RESOURCE"[@id='%s']/"XML_LRM_TAG_RSC_OP"[@id='%s' and @"XML_LRM_ATTR_CALLID"='%d']"
static void
-delete_op_entry(lrmd_event_data_t * op, const char *rsc_id, const char *key, int call_id)
+delete_op_entry(lrm_state_t *lrm_state, lrmd_event_data_t * op, const char *rsc_id, const char *key, int call_id)
{
xmlNode *xml_top = NULL;
if (op != NULL) {
xml_top = create_xml_node(NULL, XML_LRM_TAG_RSC_OP);
crm_xml_add_int(xml_top, XML_LRM_ATTR_CALLID, op->call_id);
crm_xml_add(xml_top, XML_ATTR_TRANSITION_KEY, op->user_data);
if(op->interval > 0) {
char *op_id = generate_op_key(op->rsc_id, op->op_type, op->interval);
/* Avoid deleting last_failure too (if it was a result of this recurring op failing) */
crm_xml_add(xml_top, XML_ATTR_ID, op_id);
free(op_id);
}
crm_debug("async: Sending delete op for %s_%s_%d (call=%d)",
op->rsc_id, op->op_type, op->interval, op->call_id);
fsa_cib_conn->cmds->delete(fsa_cib_conn, XML_CIB_TAG_STATUS, xml_top, cib_quorum_override);
} else if (rsc_id != NULL && key != NULL) {
int max = 0;
char *op_xpath = NULL;
if (call_id > 0) {
max =
- strlen(op_call_template) + strlen(rsc_id) + strlen(fsa_our_uname) + strlen(key) +
+ strlen(op_call_template) + strlen(rsc_id) + strlen(lrm_state->node_name) + strlen(key) +
10;
op_xpath = calloc(1, max);
- snprintf(op_xpath, max, op_call_template, fsa_our_uname, rsc_id, key, call_id);
+ snprintf(op_xpath, max, op_call_template, lrm_state->node_name, rsc_id, key, call_id);
} else {
- max = strlen(op_template) + strlen(rsc_id) + strlen(fsa_our_uname) + strlen(key) + 1;
+ max = strlen(op_template) + strlen(rsc_id) + strlen(lrm_state->node_name) + strlen(key) + 1;
op_xpath = calloc(1, max);
- snprintf(op_xpath, max, op_template, fsa_our_uname, rsc_id, key);
+ snprintf(op_xpath, max, op_template, lrm_state->node_name, rsc_id, key);
}
crm_debug("sync: Sending delete op for %s (call=%d)", rsc_id, call_id);
fsa_cib_conn->cmds->delete(fsa_cib_conn, op_xpath, NULL, cib_quorum_override | cib_xpath);
free(op_xpath);
} else {
crm_err("Not enough information to delete op entry: rsc=%p key=%p", rsc_id, key);
return;
}
crm_log_xml_trace(xml_top, "op:cancel");
free_xml(xml_top);
}
void lrm_clear_last_failure(const char *rsc_id)
{
char *attr = NULL;
GHashTableIter iter;
+ GList *lrm_state_list = lrm_state_get_list();
+ GList *state_entry;
rsc_history_t *entry = NULL;
attr = generate_op_key(rsc_id, "last_failure", 0);
- delete_op_entry(NULL, rsc_id, attr, 0);
- free(attr);
- if (!resource_history) {
- return;
- }
+ /* This clears last failure for every lrm state that has this rsc.*/
+ for (state_entry = lrm_state_list; state_entry != NULL; state_entry = state_entry->next) {
+ lrm_state_t *lrm_state = state_entry->data;
- g_hash_table_iter_init(&iter, resource_history);
- while (g_hash_table_iter_next(&iter, NULL, (void **)&entry)) {
- if (safe_str_eq(rsc_id, entry->id)) {
- lrmd_free_event(entry->failed);
- entry->failed = NULL;
+ delete_op_entry(lrm_state, NULL, rsc_id, attr, 0);
+
+ if (!lrm_state->resource_history) {
+ continue;
}
+
+ g_hash_table_iter_init(&iter, lrm_state->resource_history);
+ while (g_hash_table_iter_next(&iter, NULL, (void **)&entry)) {
+ if (safe_str_eq(rsc_id, entry->id)) {
+ lrmd_free_event(entry->failed);
+ entry->failed = NULL;
+ }
+ }
}
+ free(attr);
+
}
static gboolean
-cancel_op(const char *rsc_id, const char *key, int op, gboolean remove)
+cancel_op(lrm_state_t *lrm_state, const char *rsc_id, const char *key, int op, gboolean remove)
{
int rc = pcmk_ok;
struct recurring_op_s *pending = NULL;
CRM_CHECK(op != 0, return FALSE);
CRM_CHECK(rsc_id != NULL, return FALSE);
if (key == NULL) {
key = make_stop_id(rsc_id, op);
}
- pending = g_hash_table_lookup(pending_ops, key);
+ pending = g_hash_table_lookup(lrm_state->pending_ops, key);
if (pending) {
if (remove && pending->remove == FALSE) {
pending->remove = TRUE;
crm_debug("Scheduling %s for removal", key);
}
if (pending->cancelled) {
crm_debug("Operation %s already cancelled", key);
return TRUE;
}
pending->cancelled = TRUE;
} else {
crm_info("No pending op found for %s", key);
return TRUE;
}
crm_debug("Cancelling op %d for %s (%s)", op, rsc_id, key);
- rc = fsa_lrm_conn->cmds->cancel(fsa_lrm_conn,
+ rc = lrm_state->conn->cmds->cancel(lrm_state->conn,
pending->rsc_id,
pending->op_type,
pending->interval);
if (rc == pcmk_ok) {
crm_debug("Op %d for %s (%s): cancelled", op, rsc_id, key);
} else {
crm_debug("Op %d for %s (%s): Nothing to cancel", op, rsc_id, key);
/* The caller needs to make sure the entry is
* removed from the pending_ops list
*
* Usually by returning TRUE inside the worker function
* supplied to g_hash_table_foreach_remove()
*
* Not removing the entry from pending_ops will block
* the node from shutting down
*/
return FALSE;
}
return TRUE;
}
struct cancel_data {
gboolean done;
gboolean remove;
const char *key;
lrmd_rsc_info_t *rsc;
+ lrm_state_t *lrm_state;
};
static gboolean
cancel_action_by_key(gpointer key, gpointer value, gpointer user_data)
{
struct cancel_data *data = user_data;
struct recurring_op_s *op = (struct recurring_op_s *)value;
if (safe_str_eq(op->op_key, data->key)) {
data->done = TRUE;
- if (cancel_op(data->rsc->id, key, op->call_id, data->remove) == FALSE) {
+ if (cancel_op(data->lrm_state, data->rsc->id, key, op->call_id, data->remove) == FALSE) {
return TRUE;
}
}
return FALSE;
}
static gboolean
-cancel_op_key(lrmd_rsc_info_t * rsc, const char *key, gboolean remove)
+cancel_op_key(lrm_state_t *lrm_state, lrmd_rsc_info_t * rsc, const char *key, gboolean remove)
{
struct cancel_data data;
CRM_CHECK(rsc != NULL, return FALSE);
CRM_CHECK(key != NULL, return FALSE);
data.key = key;
data.rsc = rsc;
data.done = FALSE;
data.remove = remove;
+ data.lrm_state = lrm_state;
- g_hash_table_foreach_remove(pending_ops, cancel_action_by_key, &data);
+ g_hash_table_foreach_remove(lrm_state->pending_ops, cancel_action_by_key, &data);
return data.done;
}
static lrmd_rsc_info_t *
-get_lrm_resource(xmlNode * resource, xmlNode * op_msg, gboolean do_create)
+get_lrm_resource(lrm_state_t *lrm_state, xmlNode * resource, xmlNode * op_msg, gboolean do_create)
{
lrmd_rsc_info_t *rsc = NULL;
const char *id = ID(resource);
const char *type = crm_element_value(resource, XML_ATTR_TYPE);
const char *class = crm_element_value(resource, XML_AGENT_ATTR_CLASS);
const char *provider = crm_element_value(resource, XML_AGENT_ATTR_PROVIDER);
const char *long_id = crm_element_value(resource, XML_ATTR_ID_LONG);
crm_trace("Retrieving %s from the LRM.", id);
CRM_CHECK(id != NULL, return NULL);
- rsc = fsa_lrm_conn->cmds->get_rsc_info(fsa_lrm_conn, id, 0);
+ rsc = lrm_state->conn->cmds->get_rsc_info(lrm_state->conn, id, 0);
if (!rsc && long_id) {
- rsc = fsa_lrm_conn->cmds->get_rsc_info(fsa_lrm_conn, long_id, 0);
+ rsc = lrm_state->conn->cmds->get_rsc_info(lrm_state->conn, long_id, 0);
}
if (!rsc && do_create) {
CRM_CHECK(class != NULL, return NULL);
CRM_CHECK(type != NULL, return NULL);
crm_trace("Adding rsc %s before operation", id);
- fsa_lrm_conn->cmds->register_rsc(fsa_lrm_conn,
+ lrm_state->conn->cmds->register_rsc(lrm_state->conn,
id, class, provider, type, lrmd_opt_drop_recurring);
- rsc = fsa_lrm_conn->cmds->get_rsc_info(fsa_lrm_conn, id, 0);
+ rsc = lrm_state->conn->cmds->get_rsc_info(lrm_state->conn, id, 0);
if (!rsc) {
fsa_data_t *msg_data = NULL;
crm_err("Could not add resource %s to LRM", id);
register_fsa_error(C_FSA_INTERNAL, I_FAIL, NULL);
}
}
return rsc;
}
static void
-delete_resource(const char *id, lrmd_rsc_info_t * rsc, GHashTableIter *gIter,
- const char *sys, const char *host, const char *user, ha_msg_input_t * request)
+delete_resource(lrm_state_t *lrm_state,
+ const char *id,
+ lrmd_rsc_info_t * rsc,
+ GHashTableIter *gIter,
+ const char *sys,
+ const char *host,
+ const char *user,
+ ha_msg_input_t * request)
{
int rc = pcmk_ok;
crm_info("Removing resource %s for %s (%s) on %s", id, sys, user ? user : "internal", host);
if (rsc) {
- rc = fsa_lrm_conn->cmds->unregister_rsc(fsa_lrm_conn, id, 0);
+ rc = lrm_state->conn->cmds->unregister_rsc(lrm_state->conn, id, 0);
}
if (rc == pcmk_ok) {
crm_trace("Resource '%s' deleted", id);
} else if (rc == -EINPROGRESS) {
crm_info("Deletion of resource '%s' pending", id);
if (request) {
struct pending_deletion_op_s *op = NULL;
char *ref = crm_element_value_copy(request->msg, XML_ATTR_REFERENCE);
op = calloc(1, sizeof(struct pending_deletion_op_s));
op->rsc = strdup(rsc->id);
op->input = copy_ha_msg_input(request);
- g_hash_table_insert(deletion_ops, ref, op);
+ g_hash_table_insert(lrm_state->deletion_ops, ref, op);
}
return;
} else {
crm_warn("Deletion of resource '%s' for %s (%s) on %s failed: %d",
id, sys, user ? user : "internal", host, rc);
}
- delete_rsc_entry(request, id, gIter, rc, user);
+ delete_rsc_entry(lrm_state, request, id, gIter, rc, user);
}
/* A_LRM_INVOKE */
void
do_lrm_invoke(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input, fsa_data_t * msg_data)
{
gboolean done = FALSE;
gboolean create_rsc = TRUE;
+ lrm_state_t *lrm_state = NULL;
const char *crm_op = NULL;
const char *from_sys = NULL;
const char *from_host = NULL;
const char *operation = NULL;
ha_msg_input_t *input = fsa_typed_data(fsa_dt_ha_msg);
const char *user_name = NULL;
+ /* TODO lrm_state. Once lrmd connection resources are enabled and controlled by the
+ * pengine, the input xml should tell us what lrm_state object we should be using here.
+ * Until that work is done, we assume the local connection for now. */
+ lrm_state = lrm_state_find(fsa_our_uname);
+
#if ENABLE_ACL
user_name = crm_element_value(input->msg, F_CRM_USER);
crm_trace("LRM command from user '%s'", user_name);
#endif
crm_op = crm_element_value(input->msg, F_CRM_TASK);
from_sys = crm_element_value(input->msg, F_CRM_SYS_FROM);
if (safe_str_neq(from_sys, CRM_SYSTEM_TENGINE)) {
from_host = crm_element_value(input->msg, F_CRM_HOST_FROM);
}
crm_trace("LRM command from: %s", from_sys);
if (safe_str_eq(crm_op, CRM_OP_LRM_DELETE)) {
operation = CRMD_ACTION_DELETE;
- } else if (safe_str_eq(operation, CRM_OP_LRM_REFRESH)) {
- crm_op = CRM_OP_LRM_REFRESH;
+ } else if (safe_str_eq(crm_op, CRM_OP_LRM_REFRESH)) {
+ operation = CRM_OP_LRM_REFRESH;
} else if (safe_str_eq(crm_op, CRM_OP_LRM_FAIL)) {
rsc_history_t *entry = NULL;
lrmd_event_data_t *op = NULL;
lrmd_rsc_info_t *rsc = NULL;
xmlNode *xml_rsc = find_xml_node(input->xml, XML_CIB_TAG_RESOURCE, TRUE);
CRM_CHECK(xml_rsc != NULL, return);
/* The lrmd can not fail a resource, it does not understand the
* concept of success or failure in relation to a resource, it simply
* executes operations and reports the results. We determine what a failure is.
* Becaues of this, if we want to fail a resource we have to fake what we
* understand a failure to look like.
*
* To do this we create a fake lrmd operation event for the resource
* we want to fail. We then pass that event to the lrmd client callback
* so it will be processed as if it actually came from the lrmd. */
- op = construct_op(input->xml, ID(xml_rsc), "asyncmon");
+ op = construct_op(lrm_state, input->xml, ID(xml_rsc), "asyncmon");
free((char *) op->user_data);
op->user_data = NULL;
- entry = g_hash_table_lookup(resource_history, op->rsc_id);
+ entry = g_hash_table_lookup(lrm_state->resource_history, op->rsc_id);
/* Make sure the call id is greater than the last successful operation,
* otherwise the failure will not result in a possible recovery of the resource
* as it could appear the failure occurred before the successful start */
if (entry && entry->last) {
op->call_id = entry->last->call_id + 1;
if (op->call_id < 0) {
op->call_id = 1;
}
}
op->interval = 0;
op->op_status = PCMK_LRM_OP_DONE;
op->rc = PCMK_EXECRA_UNKNOWN_ERROR;
CRM_ASSERT(op != NULL);
# if ENABLE_ACL
if (user_name && is_privileged(user_name) == FALSE) {
crm_err("%s does not have permission to fail %s", user_name, ID(xml_rsc));
send_direct_ack(from_host, from_sys, NULL, op, ID(xml_rsc));
lrmd_free_event(op);
return;
}
# endif
- rsc = get_lrm_resource(xml_rsc, input->xml, create_rsc);
+ rsc = get_lrm_resource(lrm_state, xml_rsc, input->xml, create_rsc);
if (rsc) {
crm_info("Failing resource %s...", rsc->id);
- process_lrm_event(op);
+ process_lrm_event(lrm_state, op);
op->op_status = PCMK_LRM_OP_DONE;
op->rc = PCMK_EXECRA_OK;
lrmd_free_rsc_info(rsc);
} else {
crm_info("Cannot find/create resource in order to fail it...");
crm_log_xml_warn(input->msg, "bad input");
}
send_direct_ack(from_host, from_sys, NULL, op, ID(xml_rsc));
lrmd_free_event(op);
return;
} else if (input->xml != NULL) {
operation = crm_element_value(input->xml, XML_LRM_ATTR_TASK);
}
if (safe_str_eq(crm_op, CRM_OP_LRM_REFRESH)) {
int rc = pcmk_ok;
- xmlNode *fragment = do_lrm_query(TRUE);
+ xmlNode *fragment = do_lrm_query_internal(lrm_state, TRUE);
fsa_cib_update(XML_CIB_TAG_STATUS, fragment, cib_quorum_override, rc, user_name);
crm_info("Forced a local LRM refresh: call=%d", rc);
free_xml(fragment);
} else if (safe_str_eq(crm_op, CRM_OP_LRM_QUERY)) {
- xmlNode *data = do_lrm_query(FALSE);
+ xmlNode *data = do_lrm_query_internal(lrm_state, FALSE);
xmlNode *reply = create_reply(input->msg, data);
if (relay_message(reply, TRUE) == FALSE) {
crm_err("Unable to route reply");
crm_log_xml_err(reply, "reply");
}
free_xml(reply);
free_xml(data);
} else if (safe_str_eq(operation, CRM_OP_PROBED)) {
update_attrd(NULL, CRM_OP_PROBED, XML_BOOLEAN_TRUE, user_name);
} else if (safe_str_eq(crm_op, CRM_OP_REPROBE)) {
GHashTableIter gIter;
rsc_history_t *entry = NULL;
crm_notice("Forcing the status of all resources to be redetected");
- g_hash_table_iter_init(&gIter, resource_history);
+ g_hash_table_iter_init(&gIter, lrm_state->resource_history);
while (g_hash_table_iter_next(&gIter, NULL, (void **)&entry)) {
- delete_resource(entry->id, &entry->rsc, &gIter, from_sys, from_host, user_name, NULL);
+ delete_resource(lrm_state, entry->id, &entry->rsc, &gIter, from_sys, from_host, user_name, NULL);
}
/* Now delete the copy in the CIB */
- erase_status_tag(fsa_our_uname, XML_CIB_TAG_LRM, cib_scope_local);
+ erase_status_tag(lrm_state->node_name, XML_CIB_TAG_LRM, cib_scope_local);
/* And finally, _delete_ the value in attrd
* Setting it to FALSE results in the PE sending us back here again
*/
update_attrd(NULL, CRM_OP_PROBED, NULL, user_name);
} else if (operation != NULL) {
lrmd_rsc_info_t *rsc = NULL;
xmlNode *params = NULL;
xmlNode *xml_rsc = find_xml_node(input->xml, XML_CIB_TAG_RESOURCE, TRUE);
CRM_CHECK(xml_rsc != NULL, return);
/* only the first 16 chars are used by the LRM */
params = find_xml_node(input->xml, XML_TAG_ATTRS, TRUE);
if (safe_str_eq(operation, CRMD_ACTION_DELETE)) {
create_rsc = FALSE;
}
- rsc = get_lrm_resource(xml_rsc, input->xml, create_rsc);
+ rsc = get_lrm_resource(lrm_state, xml_rsc, input->xml, create_rsc);
if (rsc == NULL && create_rsc) {
crm_err("Invalid resource definition");
crm_log_xml_warn(input->msg, "bad input");
} else if (rsc == NULL) {
lrmd_event_data_t *op = NULL;
crm_notice("Not creating resource for a %s event: %s", operation, ID(input->xml));
- delete_rsc_entry(input, ID(xml_rsc), NULL, pcmk_ok, user_name);
+ delete_rsc_entry(lrm_state, input, ID(xml_rsc), NULL, pcmk_ok, user_name);
- op = construct_op(input->xml, ID(xml_rsc), operation);
+ op = construct_op(lrm_state, input->xml, ID(xml_rsc), operation);
op->op_status = PCMK_LRM_OP_DONE;
op->rc = PCMK_EXECRA_OK;
CRM_ASSERT(op != NULL);
send_direct_ack(from_host, from_sys, NULL, op, ID(xml_rsc));
lrmd_free_event(op);
} else if (safe_str_eq(operation, CRMD_ACTION_CANCEL)) {
lrmd_event_data_t *op = NULL;
char *op_key = NULL;
char *meta_key = NULL;
int call = 0;
const char *call_id = NULL;
const char *op_task = NULL;
const char *op_interval = NULL;
CRM_CHECK(params != NULL, crm_log_xml_warn(input->xml, "Bad command");
return);
meta_key = crm_meta_name(XML_LRM_ATTR_INTERVAL);
op_interval = crm_element_value(params, meta_key);
free(meta_key);
meta_key = crm_meta_name(XML_LRM_ATTR_TASK);
op_task = crm_element_value(params, meta_key);
free(meta_key);
meta_key = crm_meta_name(XML_LRM_ATTR_CALLID);
call_id = crm_element_value(params, meta_key);
free(meta_key);
CRM_CHECK(op_task != NULL, crm_log_xml_warn(input->xml, "Bad command");
return);
CRM_CHECK(op_interval != NULL, crm_log_xml_warn(input->xml, "Bad command");
return);
- op = construct_op(input->xml, rsc->id, op_task);
+ op = construct_op(lrm_state, input->xml, rsc->id, op_task);
CRM_ASSERT(op != NULL);
op_key = generate_op_key(rsc->id, op_task, crm_parse_int(op_interval, "0"));
crm_debug("PE requested op %s (call=%s) be cancelled",
op_key, call_id ? call_id : "NA");
call = crm_parse_int(call_id, "0");
if (call == 0) {
/* the normal case when the PE cancels a recurring op */
- done = cancel_op_key(rsc, op_key, TRUE);
+ done = cancel_op_key(lrm_state, rsc, op_key, TRUE);
} else {
/* the normal case when the PE cancels an orphan op */
- done = cancel_op(rsc->id, NULL, call, TRUE);
+ done = cancel_op(lrm_state, rsc->id, NULL, call, TRUE);
}
if (done == FALSE) {
crm_debug("Nothing known about operation %d for %s", call, op_key);
- delete_op_entry(NULL, rsc->id, op_key, call);
+ delete_op_entry(lrm_state, NULL, rsc->id, op_key, call);
/* needed?? surely not otherwise the cancel_op_(_key) wouldn't
* have failed in the first place
*/
- g_hash_table_remove(pending_ops, op_key);
+ g_hash_table_remove(lrm_state->pending_ops, op_key);
}
op->rc = PCMK_EXECRA_OK;
op->op_status = PCMK_LRM_OP_DONE;
send_direct_ack(from_host, from_sys, rsc, op, rsc->id);
free(op_key);
lrmd_free_event(op);
} else if (safe_str_eq(operation, CRMD_ACTION_DELETE)) {
int cib_rc = pcmk_ok;
CRM_ASSERT(rsc != NULL);
- cib_rc = delete_rsc_status(rsc->id, cib_dryrun | cib_sync_call, user_name);
+ cib_rc = delete_rsc_status(lrm_state, rsc->id, cib_dryrun | cib_sync_call, user_name);
if (cib_rc != pcmk_ok) {
lrmd_event_data_t *op = NULL;
crm_err
("Attempt of deleting resource status '%s' from CIB for %s (user=%s) on %s failed: (rc=%d) %s",
rsc->id, from_sys, user_name ? user_name : "unknown", from_host, cib_rc,
pcmk_strerror(cib_rc));
- op = construct_op(input->xml, rsc->id, operation);
+ op = construct_op(lrm_state, input->xml, rsc->id, operation);
op->op_status = PCMK_LRM_OP_ERROR;
if (cib_rc == -EACCES) {
op->rc = PCMK_EXECRA_INSUFFICIENT_PRIV;
} else {
op->rc = PCMK_EXECRA_UNKNOWN_ERROR;
}
send_direct_ack(from_host, from_sys, NULL, op, rsc->id);
lrmd_free_event(op);
return;
}
- delete_resource(rsc->id, rsc, NULL, from_sys, from_host, user_name, input);
+ delete_resource(lrm_state, rsc->id, rsc, NULL, from_sys, from_host, user_name, input);
} else if (rsc != NULL) {
- do_lrm_rsc_op(rsc, operation, input->xml, input->msg);
+ do_lrm_rsc_op(lrm_state, rsc, operation, input->xml, input->msg);
}
lrmd_free_rsc_info(rsc);
} else {
crm_err("Operation was neither a lrm_query, nor a rsc op. %s", crm_str(crm_op));
register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
}
}
static lrmd_event_data_t *
-construct_op(xmlNode * rsc_op, const char *rsc_id, const char *operation)
+construct_op(lrm_state_t *lrm_state, xmlNode * rsc_op, const char *rsc_id, const char *operation)
{
lrmd_event_data_t *op = NULL;
const char *op_delay = NULL;
const char *op_timeout = NULL;
const char *op_interval = NULL;
GHashTable *params = NULL;
const char *transition = NULL;
CRM_LOG_ASSERT(rsc_id != NULL);
op = calloc(1, sizeof(lrmd_event_data_t));
op->type = lrmd_event_exec_complete;
op->op_type = strdup(operation);
op->op_status = PCMK_LRM_OP_PENDING;
op->rc = -1;
op->rsc_id = strdup(rsc_id);
op->interval = 0;
op->timeout = 0;
op->start_delay = 0;
if (rsc_op == NULL) {
CRM_LOG_ASSERT(safe_str_eq(CRMD_ACTION_STOP, operation));
op->user_data = NULL;
/* the stop_all_resources() case
* by definition there is no DC (or they'd be shutting
* us down).
* So we should put our version here.
*/
op->params = g_hash_table_new_full(crm_str_hash, g_str_equal,
g_hash_destroy_str, g_hash_destroy_str);
g_hash_table_insert(op->params,
strdup(XML_ATTR_CRM_VERSION), strdup(CRM_FEATURE_SET));
crm_trace("Constructed %s op for %s", operation, rsc_id);
return op;
}
params = xml2list(rsc_op);
g_hash_table_remove(params, CRM_META "_op_target_rc");
op_delay = crm_meta_value(params, XML_OP_ATTR_START_DELAY);
op_timeout = crm_meta_value(params, XML_ATTR_TIMEOUT);
op_interval = crm_meta_value(params, XML_LRM_ATTR_INTERVAL);
op->interval = crm_parse_int(op_interval, "0");
op->timeout = crm_parse_int(op_timeout, "0");
op->start_delay = crm_parse_int(op_delay, "0");
if (safe_str_neq(operation, RSC_STOP)) {
op->params = params;
} else {
- rsc_history_t *entry = g_hash_table_lookup(resource_history, rsc_id);
+ rsc_history_t *entry = g_hash_table_lookup(lrm_state->resource_history, rsc_id);
/* If we do not have stop parameters cached, use
* whatever we are given */
if (!entry || !entry->stop_params) {
op->params = params;
} else {
/* Copy the cached parameter list so that we stop the resource
* with the old attributes, not the new ones */
op->params = g_hash_table_new_full(crm_str_hash, g_str_equal,
g_hash_destroy_str, g_hash_destroy_str);
g_hash_table_foreach(params, copy_meta_keys, op->params);
g_hash_table_foreach(entry->stop_params, copy_instance_keys, op->params);
g_hash_table_destroy(params);
params = NULL;
}
}
/* sanity */
if (op->interval < 0) {
op->interval = 0;
}
if (op->timeout <= 0) {
op->timeout = op->interval;
}
if (op->start_delay < 0) {
op->start_delay = 0;
}
transition = crm_element_value(rsc_op, XML_ATTR_TRANSITION_KEY);
CRM_CHECK(transition != NULL, return op);
op->user_data = strdup(transition);
if (op->interval != 0) {
if (safe_str_eq(operation, CRMD_ACTION_START)
|| safe_str_eq(operation, CRMD_ACTION_STOP)) {
crm_err("Start and Stop actions cannot have an interval: %d", op->interval);
op->interval = 0;
}
}
crm_trace("Constructed %s op for %s: interval=%d", operation, rsc_id, op->interval);
return op;
}
void
send_direct_ack(const char *to_host, const char *to_sys,
lrmd_rsc_info_t * rsc, lrmd_event_data_t * op, const char *rsc_id)
{
xmlNode *reply = NULL;
xmlNode *update, *iter;
xmlNode *fragment;
crm_node_t *peer = NULL;
CRM_CHECK(op != NULL, return);
if (op->rsc_id == NULL) {
CRM_LOG_ASSERT(rsc_id != NULL);
op->rsc_id = strdup(rsc_id);
}
if (to_sys == NULL) {
to_sys = CRM_SYSTEM_TENGINE;
}
peer = crm_get_peer(0, fsa_our_uname);
update = do_update_node_cib(peer, node_update_none, NULL, __FUNCTION__);
iter = create_xml_node(update, XML_CIB_TAG_LRM);
crm_xml_add(iter, XML_ATTR_ID, fsa_our_uuid);
iter = create_xml_node(iter, XML_LRM_TAG_RESOURCES);
iter = create_xml_node(iter, XML_LRM_TAG_RESOURCE);
crm_xml_add(iter, XML_ATTR_ID, op->rsc_id);
build_operation_update(iter, rsc, op, __FUNCTION__);
fragment = create_cib_fragment(update, XML_CIB_TAG_STATUS);
reply = create_request(CRM_OP_INVOKE_LRM, fragment, to_host, to_sys, CRM_SYSTEM_LRMD, NULL);
crm_log_xml_trace(update, "ACK Update");
crm_debug("ACK'ing resource op %s_%s_%d from %s: %s",
op->rsc_id, op->op_type, op->interval, op->user_data,
crm_element_value(reply, XML_ATTR_REFERENCE));
if (relay_message(reply, TRUE) == FALSE) {
crm_log_xml_err(reply, "Unable to route reply");
}
free_xml(fragment);
free_xml(update);
free_xml(reply);
}
+gboolean
+verify_stopped(enum crmd_fsa_state cur_state, int log_level)
+{
+ gboolean res = TRUE;
+ GList *lrm_state_list = lrm_state_get_list();
+ GList *state_entry;
+
+ for (state_entry = lrm_state_list; state_entry != NULL; state_entry = state_entry->next) {
+ lrm_state_t *lrm_state = state_entry->data;
+
+ if (!lrm_state_verify_stopped(lrm_state, cur_state, log_level)) {
+ /* keep iterating through all even when false is returned */
+ res = FALSE;
+ }
+ }
+
+ set_bit(fsa_input_register, R_SENT_RSC_STOP);
+ return res;
+}
+
+struct stop_recurring_action_s {
+ lrmd_rsc_info_t *rsc;
+ lrm_state_t *lrm_state;
+};
+
static gboolean
stop_recurring_action_by_rsc(gpointer key, gpointer value, gpointer user_data)
{
- lrmd_rsc_info_t *rsc = user_data;
+ struct stop_recurring_action_s *event = user_data;
struct recurring_op_s *op = (struct recurring_op_s *)value;
- if (op->interval != 0 && safe_str_eq(op->rsc_id, rsc->id)) {
- if (cancel_op(rsc->id, key, op->call_id, FALSE) == FALSE) {
+ if (op->interval != 0 && safe_str_eq(op->rsc_id, event->rsc->id)) {
+ if (cancel_op(event->lrm_state, event->rsc->id, key, op->call_id, FALSE) == FALSE) {
return TRUE;
}
}
return FALSE;
}
static gboolean
stop_recurring_actions(gpointer key, gpointer value, gpointer user_data)
{
- gboolean remove = FALSE;
+ lrm_state_t *lrm_state = user_data;
struct recurring_op_s *op = (struct recurring_op_s *)value;
+ gboolean remove = FALSE;
if (op->interval != 0) {
- remove = cancel_op(op->rsc_id, key, op->call_id, FALSE);
+ remove = cancel_op(lrm_state, op->rsc_id, key, op->call_id, FALSE);
}
return remove;
}
-void
-do_lrm_rsc_op(lrmd_rsc_info_t * rsc, const char *operation, xmlNode * msg, xmlNode * request)
+static void
+do_lrm_rsc_op(lrm_state_t *lrm_state, lrmd_rsc_info_t * rsc, const char *operation, xmlNode * msg, xmlNode * request)
{
int call_id = 0;
char *op_id = NULL;
lrmd_event_data_t *op = NULL;
lrmd_key_value_t *params = NULL;
fsa_data_t *msg_data = NULL;
const char *transition = NULL;
CRM_CHECK(rsc != NULL, return);
if (msg != NULL) {
transition = crm_element_value(msg, XML_ATTR_TRANSITION_KEY);
if (transition == NULL) {
crm_log_xml_err(msg, "Missing transition number");
}
}
- op = construct_op(msg, rsc->id, operation);
+ op = construct_op(lrm_state, msg, rsc->id, operation);
/* stop the monitor before stopping the resource */
if (crm_str_eq(operation, CRMD_ACTION_STOP, TRUE)
|| crm_str_eq(operation, CRMD_ACTION_DEMOTE, TRUE)
|| crm_str_eq(operation, CRMD_ACTION_PROMOTE, TRUE)
|| crm_str_eq(operation, CRMD_ACTION_MIGRATE, TRUE)) {
- g_hash_table_foreach_remove(pending_ops, stop_recurring_action_by_rsc, rsc);
+
+
+ struct stop_recurring_action_s data;
+ data.rsc = rsc;
+ data.lrm_state = lrm_state;
+ g_hash_table_foreach_remove(lrm_state->pending_ops, stop_recurring_action_by_rsc, &data);
}
/* now do the op */
crm_debug("Performing key=%s op=%s_%s_%d", transition, rsc->id, operation, op->interval);
if (fsa_state != S_NOT_DC && fsa_state != S_POLICY_ENGINE && fsa_state != S_TRANSITION_ENGINE) {
if (safe_str_neq(operation, "fail")
&& safe_str_neq(operation, CRMD_ACTION_STOP)) {
crm_info("Discarding attempt to perform action %s on %s"
" in state %s", operation, rsc->id, fsa_state2string(fsa_state));
op->rc = 99;
op->op_status = PCMK_LRM_OP_ERROR;
send_direct_ack(NULL, NULL, rsc, op, rsc->id);
lrmd_free_event(op);
free(op_id);
return;
}
}
op_id = generate_op_key(rsc->id, op->op_type, op->interval);
if (op->interval > 0) {
/* cancel it so we can then restart it without conflict */
- cancel_op_key(rsc, op_id, FALSE);
+ cancel_op_key(lrm_state, rsc, op_id, FALSE);
}
if (op->params) {
char *key = NULL;
char *value = NULL;
GHashTableIter iter;
g_hash_table_iter_init(&iter, op->params);
while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value)) {
params = lrmd_key_value_add(params, key, value);
}
}
- call_id = fsa_lrm_conn->cmds->exec(fsa_lrm_conn,
+ call_id = lrm_state->conn->cmds->exec(lrm_state->conn,
rsc->id,
op->op_type,
op->user_data,
op->interval,
op->timeout,
op->start_delay,
lrmd_opt_notify_changes_only,
params);
if (call_id <= 0) {
crm_err("Operation %s on %s failed: %d", operation, rsc->id, call_id);
register_fsa_error(C_FSA_INTERNAL, I_FAIL, NULL);
} else {
/* record all operations so we can wait
* for them to complete during shutdown
*/
char *call_id_s = make_stop_id(rsc->id, call_id);
struct recurring_op_s *pending = NULL;
pending = calloc(1, sizeof(struct recurring_op_s));
crm_trace("Recording pending op: %d - %s %s", call_id, op_id, call_id_s);
pending->call_id = call_id;
pending->interval = op->interval;
pending->op_type = strdup(operation);
pending->op_key = strdup(op_id);
pending->rsc_id = strdup(rsc->id);
- g_hash_table_replace(pending_ops, call_id_s, pending);
+ g_hash_table_replace(lrm_state->pending_ops, call_id_s, pending);
if (op->interval > 0 && op->start_delay > START_DELAY_THRESHOLD) {
char *uuid = NULL;
int dummy = 0, target_rc = 0;
crm_info("Faking confirmation of %s: execution postponed for over 5 minutes", op_id);
decode_transition_key(op->user_data, &uuid, &dummy, &dummy, &target_rc);
free(uuid);
op->rc = target_rc;
op->op_status = PCMK_LRM_OP_DONE;
send_direct_ack(NULL, NULL, rsc, op, rsc->id);
}
}
free(op_id);
lrmd_free_event(op);
return;
}
int last_resource_update = 0;
static void
cib_rsc_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
{
switch (rc) {
case pcmk_ok:
case -pcmk_err_diff_failed:
case -pcmk_err_diff_resync:
crm_trace("Resource update %d complete: rc=%d", call_id, rc);
break;
default:
crm_warn("Resource update %d failed: (rc=%d) %s", call_id, rc, pcmk_strerror(rc));
}
if(call_id == last_resource_update) {
last_resource_update = 0;
trigger_fsa(fsa_source);
}
}
static int
-do_update_resource(lrmd_rsc_info_t * rsc, lrmd_event_data_t * op)
+do_update_resource(lrm_state_t *lrm_state, lrmd_rsc_info_t * rsc, lrmd_event_data_t * op)
{
/*
<status>
<nodes_status id=uname>
<lrm>
<lrm_resources>
<lrm_resource id=...>
</...>
*/
int rc = pcmk_ok;
xmlNode *update, *iter = NULL;
int call_opt = cib_quorum_override;
CRM_CHECK(op != NULL, return 0);
if (fsa_state == S_ELECTION || fsa_state == S_PENDING) {
crm_info("Sending update to local CIB in state: %s", fsa_state2string(fsa_state));
call_opt |= cib_scope_local;
}
iter = create_xml_node(iter, XML_CIB_TAG_STATUS);
update = iter;
iter = create_xml_node(iter, XML_CIB_TAG_STATE);
- set_uuid(iter, XML_ATTR_UUID, fsa_our_uname);
- crm_xml_add(iter, XML_ATTR_UNAME, fsa_our_uname);
+ set_uuid(iter, XML_ATTR_UUID, lrm_state->node_name);
+ crm_xml_add(iter, XML_ATTR_UNAME, lrm_state->node_name);
crm_xml_add(iter, XML_ATTR_ORIGIN, __FUNCTION__);
iter = create_xml_node(iter, XML_CIB_TAG_LRM);
crm_xml_add(iter, XML_ATTR_ID, fsa_our_uuid);
iter = create_xml_node(iter, XML_LRM_TAG_RESOURCES);
iter = create_xml_node(iter, XML_LRM_TAG_RESOURCE);
crm_xml_add(iter, XML_ATTR_ID, op->rsc_id);
build_operation_update(iter, rsc, op, __FUNCTION__);
if (rsc) {
crm_xml_add(iter, XML_ATTR_TYPE, rsc->type);
crm_xml_add(iter, XML_AGENT_ATTR_CLASS, rsc->class);
crm_xml_add(iter, XML_AGENT_ATTR_PROVIDER, rsc->provider);
CRM_CHECK(rsc->type != NULL, crm_err("Resource %s has no value for type", op->rsc_id));
CRM_CHECK(rsc->class != NULL, crm_err("Resource %s has no value for class", op->rsc_id));
} else {
crm_warn("Resource %s no longer exists in the lrmd", op->rsc_id);
goto cleanup;
}
/* make it an asyncronous call and be done with it
*
* Best case:
* the resource state will be discovered during
* the next signup or election.
*
* Bad case:
* we are shutting down and there is no DC at the time,
* but then why were we shutting down then anyway?
* (probably because of an internal error)
*
* Worst case:
* we get shot for having resources "running" when the really weren't
*
* the alternative however means blocking here for too long, which
* isnt acceptable
*/
fsa_cib_update(XML_CIB_TAG_STATUS, update, call_opt, rc, NULL);
if(rc > 0) {
last_resource_update = rc;
}
/* the return code is a call number, not an error code */
crm_trace("Sent resource state update message: %d for %s=%d on %s", rc,
op->op_type, op->interval, op->rsc_id);
fsa_register_cib_callback(rc, FALSE, NULL, cib_rsc_callback);
cleanup:
free_xml(update);
return rc;
}
void
do_lrm_event(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state, enum crmd_fsa_input cur_input, fsa_data_t * msg_data)
{
CRM_CHECK(FALSE, return);
}
gboolean
-process_lrm_event(lrmd_event_data_t * op)
+process_lrm_event(lrm_state_t *lrm_state, lrmd_event_data_t * op)
{
char *op_id = NULL;
char *op_key = NULL;
int update_id = 0;
int log_level = LOG_ERR;
gboolean removed = FALSE;
lrmd_rsc_info_t *rsc = NULL;
struct recurring_op_s *pending = NULL;
CRM_CHECK(op != NULL, return FALSE);
if (op->type == lrmd_event_disconnect) {
lrm_connection_destroy();
return TRUE;
} else if (op->type != lrmd_event_exec_complete) {
return TRUE;
}
CRM_CHECK(op->rsc_id != NULL, return FALSE);
op_id = make_stop_id(op->rsc_id, op->call_id);
- pending = g_hash_table_lookup(pending_ops, op_id);
+ pending = g_hash_table_lookup(lrm_state->pending_ops, op_id);
op_key = generate_op_key(op->rsc_id, op->op_type, op->interval);
- rsc = fsa_lrm_conn->cmds->get_rsc_info(fsa_lrm_conn, op->rsc_id, 0);
+ rsc = lrm_state->conn->cmds->get_rsc_info(lrm_state->conn, op->rsc_id, 0);
switch (op->op_status) {
case PCMK_LRM_OP_ERROR:
case PCMK_LRM_OP_PENDING:
case PCMK_LRM_OP_NOTSUPPORTED:
break;
case PCMK_LRM_OP_CANCELLED:
log_level = LOG_INFO;
break;
case PCMK_LRM_OP_DONE:
log_level = LOG_NOTICE;
break;
case PCMK_LRM_OP_TIMEOUT:
log_level = LOG_DEBUG_3;
crm_err("LRM operation %s (%d) %s (timeout=%dms)",
op_key, op->call_id, services_lrm_status_str(op->op_status), op->timeout);
break;
default:
crm_err("Mapping unknown status (%d) to ERROR", op->op_status);
op->op_status = PCMK_LRM_OP_ERROR;
}
if (op->op_status == PCMK_LRM_OP_ERROR
&& (op->rc == PCMK_EXECRA_RUNNING_MASTER || op->rc == PCMK_EXECRA_NOT_RUNNING)) {
/* Leave it up to the TE/PE to decide if this is an error */
op->op_status = PCMK_LRM_OP_DONE;
log_level = LOG_INFO;
}
if (op->op_status != PCMK_LRM_OP_CANCELLED) {
if (safe_str_eq(op->op_type, RSC_NOTIFY)) {
/* Keep notify ops out of the CIB */
send_direct_ack(NULL, NULL, NULL, op, op->rsc_id);
} else {
- update_id = do_update_resource(rsc, op);
+ update_id = do_update_resource(lrm_state, rsc, op);
}
} else if (op->interval == 0) {
/* This will occur when "crm resource cleanup" is called while actions are in-flight */
crm_err("Op %s (call=%d): Cancelled", op_key, op->call_id);
send_direct_ack(NULL, NULL, NULL, op, op->rsc_id);
} else if (pending == NULL) {
/* Operations that are cancelled may safely be removed
* from the pending op list before the lrmd completion event
* is received. Only report non-cancelled ops here. */
if (op->op_status != PCMK_LRM_OP_CANCELLED) {
crm_err("Op %s (call=%d): No 'pending' entry", op_key, op->call_id);
}
} else if (op->user_data == NULL) {
crm_err("Op %s (call=%d): No user data", op_key, op->call_id);
} else if (pending->remove) {
- delete_op_entry(op, op->rsc_id, op_key, op->call_id);
+ delete_op_entry(lrm_state, op, op->rsc_id, op_key, op->call_id);
} else {
/* Before a stop is called, no need to direct ack */
crm_trace("Op %s (call=%d): no delete event required", op_key, op->call_id);
}
- if ((op->interval == 0) && g_hash_table_remove(pending_ops, op_id)) {
+ if ((op->interval == 0) && g_hash_table_remove(lrm_state->pending_ops, op_id)) {
removed = TRUE;
crm_trace("Op %s (call=%d, stop-id=%s): Confirmed", op_key, op->call_id, op_id);
}
if (op->op_status == PCMK_LRM_OP_DONE) {
do_crm_log(log_level,
"LRM operation %s (call=%d, rc=%d, cib-update=%d, confirmed=%s) %s",
op_key, op->call_id, op->rc, update_id, removed ? "true" : "false",
lrmd_event_rc2str(op->rc));
} else {
do_crm_log(log_level,
"LRM operation %s (call=%d, status=%d, cib-update=%d, confirmed=%s) %s",
op_key, op->call_id, op->op_status, update_id, removed ? "true" : "false",
services_lrm_status_str(op->op_status));
}
if (op->output) {
char *prefix = g_strdup_printf("%s_%s_%d:%d", op->rsc_id, op->op_type, op->interval, op->call_id);
if (op->rc) {
crm_log_output(LOG_NOTICE, prefix, op->output);
} else {
crm_log_output(LOG_DEBUG, prefix, op->output);
}
g_free(prefix);
}
if (op->rsc_deleted) {
crm_info("Deletion of resource '%s' complete after %s", op->rsc_id, op_key);
- delete_rsc_entry(NULL, op->rsc_id, NULL, pcmk_ok, NULL);
+ delete_rsc_entry(lrm_state, NULL, op->rsc_id, NULL, pcmk_ok, NULL);
}
/* If a shutdown was escalated while operations were pending,
* then the FSA will be stalled right now... allow it to continue
*/
mainloop_set_trigger(fsa_source);
- update_history_cache(rsc, op);
+ update_history_cache(lrm_state, rsc, op);
lrmd_free_rsc_info(rsc);
free(op_key);
free(op_id);
return TRUE;
}
+
diff --git a/crmd/lrm_state.c b/crmd/lrm_state.c
new file mode 100644
index 0000000000..e28904d5a0
--- /dev/null
+++ b/crmd/lrm_state.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2012 David Vossel <dvossel@redhat.com>
+ *
+ * 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 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <crm_internal.h>
+#include <crm/crm.h>
+
+#include <crmd.h>
+#include <crmd_fsa.h>
+#include <crmd_messages.h>
+#include <crmd_callbacks.h>
+#include <crmd_lrm.h>
+
+GHashTable *lrm_state_table = NULL;
+
+static void
+history_cache_destroy(gpointer data)
+{
+ rsc_history_t *entry = data;
+
+ if (entry->stop_params) {
+ g_hash_table_destroy(entry->stop_params);
+ }
+
+ free(entry->rsc.type);
+ free(entry->rsc.class);
+ free(entry->rsc.provider);
+
+ lrmd_free_event(entry->failed);
+ lrmd_free_event(entry->last);
+ free(entry->id);
+ free(entry);
+}
+
+static void
+free_deletion_op(gpointer value)
+{
+ struct pending_deletion_op_s *op = value;
+
+ free(op->rsc);
+ delete_ha_msg_input(op->input);
+ free(op);
+}
+
+static void
+free_recurring_op(gpointer value)
+{
+ struct recurring_op_s *op = (struct recurring_op_s *)value;
+
+ free(op->rsc_id);
+ free(op->op_type);
+ free(op->op_key);
+ free(op);
+}
+
+
+static lrm_state_t *
+internal_state_create(const char *node_name)
+{
+
+ lrm_state_t *state = calloc(1, sizeof(lrm_state_t));
+
+ if (!state) {
+ return NULL;
+ } else if (!node_name) {
+ crm_err("No node name given for lrm state object");
+ return NULL;
+ }
+
+ state->node_name = strdup(node_name);
+
+ state->deletion_ops = g_hash_table_new_full(crm_str_hash,
+ g_str_equal,
+ g_hash_destroy_str,
+ free_deletion_op);
+
+ state->pending_ops = g_hash_table_new_full(crm_str_hash,
+ g_str_equal,
+ g_hash_destroy_str,
+ free_recurring_op);
+
+ state->resource_history = g_hash_table_new_full(crm_str_hash,
+ g_str_equal,
+ NULL,
+ history_cache_destroy);
+
+ g_hash_table_insert(lrm_state_table, (char *) state->node_name, state);
+ return state;
+
+}
+static lrm_state_t *
+lrm_state_create_local(const char *node_name)
+{
+
+ lrm_state_t *state = internal_state_create(node_name);
+
+ if (state) {
+ state->conn = lrmd_api_new();
+ state->conn->cmds->set_callback(state->conn, lrm_op_callback);
+ }
+ return state;
+}
+
+lrm_state_t *
+lrm_state_create_remote(const char *node_name, const char *server, int port)
+{
+ lrm_state_t *state;
+
+ if (!server || !port) {
+ return NULL;
+ }
+
+ state = internal_state_create(node_name);
+
+ if (state) {
+ state->conn = lrmd_remote_api_new(node_name, server, port);
+ state->conn->cmds->set_callback(state->conn, lrm_op_callback);
+ }
+ return state;
+}
+
+void lrm_state_destroy(const char *node_name)
+{
+ g_hash_table_remove(lrm_state_table, node_name);
+}
+
+static void
+internal_lrm_state_destroy(gpointer data)
+{
+ lrm_state_t *lrm_state = data;
+ if (!lrm_state) {
+ return;
+ }
+
+ lrmd_api_delete(lrm_state->conn);
+
+ if (lrm_state->resource_history) {
+ g_hash_table_destroy(lrm_state->resource_history);
+ }
+ if (lrm_state->deletion_ops) {
+ g_hash_table_destroy(lrm_state->deletion_ops);
+ }
+ if (lrm_state->pending_ops) {
+ g_hash_table_destroy(lrm_state->pending_ops);
+ }
+
+ free((char *) lrm_state->node_name);
+ free(lrm_state);
+}
+
+void lrm_state_reset_tables(lrm_state_t *lrm_state)
+{
+
+ if (lrm_state->resource_history) {
+ g_hash_table_remove_all(lrm_state->resource_history);
+ }
+ if (lrm_state->deletion_ops) {
+ g_hash_table_remove_all(lrm_state->deletion_ops);
+ }
+ if (lrm_state->pending_ops) {
+ g_hash_table_remove_all(lrm_state->pending_ops);
+ }
+}
+
+gboolean lrm_state_init_local(void)
+{
+ if (lrm_state_table) {
+ return TRUE;
+ }
+
+ lrm_state_table = g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, internal_lrm_state_destroy);
+ if (!lrm_state_table) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void lrm_state_destroy_all(void)
+{
+ if (lrm_state_table) {
+ g_hash_table_destroy(lrm_state_table);
+ }
+}
+
+lrm_state_t *lrm_state_find(const char *node_name)
+{
+ return g_hash_table_lookup(lrm_state_table, node_name);
+}
+
+lrm_state_t *lrm_state_find_or_create_local(const char *node_name)
+{
+ lrm_state_t *lrm_state;
+
+ lrm_state = g_hash_table_lookup(lrm_state_table, node_name);
+ if (!lrm_state) {
+ lrm_state = lrm_state_create_local(node_name);
+ }
+
+ return lrm_state;
+}
+
+GList *lrm_state_get_list(void)
+{
+ return g_hash_table_get_values(lrm_state_table);
+}
diff --git a/include/crm/lrmd.h b/include/crm/lrmd.h
index 067461015d..a07c9757eb 100644
--- a/include/crm/lrmd.h
+++ b/include/crm/lrmd.h
@@ -1,455 +1,466 @@
/*
* Copyright (c) 2012 David Vossel <dvossel@redhat.com>
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/**
* \file
* \brief Local Resource Manager
* \ingroup lrm
*/
#ifndef LRMD__H
#define LRMD__H
typedef struct lrmd_s lrmd_t;
typedef struct lrmd_key_value_s {
char *key;
char *value;
struct lrmd_key_value_s *next;
} lrmd_key_value_t;
/* *INDENT-OFF* */
#define DEFAULT_REMOTE_KEY_LOCATION "/etc/pacemaker/authkey"
#define ALT_REMOTE_KEY_LOCATION "/etc/corosync/authkey"
#define DEFAULT_REMOTE_PORT 1984
#define DEFAULT_REMOTE_USERNAME "lrmd"
#define F_LRMD_OPERATION "lrmd_op"
#define F_LRMD_CLIENTNAME "lrmd_clientname"
#define F_LRMD_CLIENTID "lrmd_clientid"
#define F_LRMD_REMOTE_MSG_TYPE "lrmd_remote_msg_type"
#define F_LRMD_REMOTE_MSG_ID "lrmd_remote_msg_id"
#define F_LRMD_CALLBACK_TOKEN "lrmd_async_id"
#define F_LRMD_CALLID "lrmd_callid"
#define F_LRMD_CANCEL_CALLID "lrmd_cancel_callid"
#define F_LRMD_CALLOPTS "lrmd_callopt"
#define F_LRMD_CALLDATA "lrmd_calldata"
#define F_LRMD_RC "lrmd_rc"
#define F_LRMD_EXEC_RC "lrmd_exec_rc"
#define F_LRMD_OP_STATUS "lrmd_exec_op_status"
#define F_LRMD_TIMEOUT "lrmd_timeout"
#define F_LRMD_CLASS "lrmd_class"
#define F_LRMD_PROVIDER "lrmd_provider"
#define F_LRMD_TYPE "lrmd_type"
#define F_LRMD_ORIGIN "lrmd_origin"
#define F_LRMD_RSC_RUN_TIME "lrmd_run_time"
#define F_LRMD_RSC_RCCHANGE_TIME "lrmd_rcchange_time"
#define F_LRMD_RSC_EXEC_TIME "lrmd_exec_time"
#define F_LRMD_RSC_QUEUE_TIME "lrmd_queue_time"
#define F_LRMD_RSC_ID "lrmd_rsc_id"
#define F_LRMD_RSC_ACTION "lrmd_rsc_action"
#define F_LRMD_RSC_USERDATA_STR "lrmd_rsc_userdata_str"
#define F_LRMD_RSC_OUTPUT "lrmd_rsc_output"
#define F_LRMD_RSC_START_DELAY "lrmd_rsc_start_delay"
#define F_LRMD_RSC_INTERVAL "lrmd_rsc_interval"
#define F_LRMD_RSC_METADATA "lrmd_rsc_metadata_res"
#define F_LRMD_RSC_DELETED "lrmd_rsc_deleted"
#define F_LRMD_RSC "lrmd_rsc"
#define LRMD_OP_RSC_CHK_REG "lrmd_rsc_check_register"
#define LRMD_OP_RSC_REG "lrmd_rsc_register"
#define LRMD_OP_RSC_EXEC "lrmd_rsc_exec"
#define LRMD_OP_RSC_CANCEL "lrmd_rsc_cancel"
#define LRMD_OP_RSC_UNREG "lrmd_rsc_unregister"
#define LRMD_OP_RSC_INFO "lrmd_rsc_info"
#define LRMD_OP_RSC_METADATA "lrmd_rsc_metadata"
#define T_LRMD "lrmd"
#define T_LRMD_REPLY "lrmd_reply"
#define T_LRMD_NOTIFY "lrmd_notify"
/* *INDENT-ON* */
/*!
* \brief Create a new local lrmd connection
*/
lrmd_t *lrmd_api_new(void);
/*!
* \brief Create a new remote lrmd connection using tls backend
+ *
+ * \note nodename and server may be the same value.
+ *
+ * \param nodename, the remote node name identified with this connection.
+ * \param server, the server to connect to.
+ * \param port, the port to connect to.
*/
-lrmd_t *lrmd_remote_api_new(const char *server, int port);
+lrmd_t *lrmd_remote_api_new(const char *nodename, const char *server, int port);
/*!
* \brief Use after lrmd_poll returns 1.
*
* \param fd to poll on
* \param timeout in ms
*
* \retval true - connection is still up
* \retval false - disconnected
*/
bool lrmd_dispatch(lrmd_t *lrmd);
/*!
* \brief Poll for a specified timeout period to determine if a message
* is ready for dispatch.
* \retval 1 msg is ready
* \retval 0 timeout occured
* \retval negative error code
*/
int lrmd_poll(lrmd_t *lrmd, int timeout);
/*!
* \brief Destroy lrmd object
*/
void lrmd_api_delete(lrmd_t * lrmd);
lrmd_key_value_t *lrmd_key_value_add(lrmd_key_value_t *kvp,
const char *key,
const char *value);
/* *INDENT-OFF* */
/* Reserved for future use */
enum lrmd_call_options {
lrmd_opt_none = 0x00000000,
/* lrmd_opt_sync_call = 0x00000001, //Not implemented, patches welcome. */
/*! Only notify the client originating a exec() the results */
lrmd_opt_notify_orig_only = 0x00000002,
/*! Drop recurring operations initiated by a client when client disconnects.
* This call_option is only valid when registering a resource. */
lrmd_opt_drop_recurring = 0x00000003,
/*! Only send out notifications for recurring operations whenthe result changes */
lrmd_opt_notify_changes_only = 0x00000004,
};
enum lrmd_callback_event {
lrmd_event_register,
lrmd_event_unregister,
lrmd_event_exec_complete,
lrmd_event_disconnect,
};
enum lrmd_exec_rc {
PCMK_EXECRA_OK = 0,
PCMK_EXECRA_UNKNOWN_ERROR = 1,
PCMK_EXECRA_INVALID_PARAM = 2,
PCMK_EXECRA_UNIMPLEMENT_FEATURE = 3,
PCMK_EXECRA_INSUFFICIENT_PRIV = 4,
PCMK_EXECRA_NOT_INSTALLED = 5,
PCMK_EXECRA_NOT_CONFIGURED = 6,
PCMK_EXECRA_NOT_RUNNING = 7,
PCMK_EXECRA_RUNNING_MASTER = 8,
PCMK_EXECRA_FAILED_MASTER = 9,
/* For status command only */
PCMK_EXECRA_STATUS_UNKNOWN = 14,
};
/* *INDENT-ON* */
typedef struct lrmd_event_data_s {
/*! Type of event, register, unregister, call_completed... */
enum lrmd_callback_event type;
/*! The resource this event occurred on. */
const char *rsc_id;
/*! The action performed, start, stop, monitor... */
const char *op_type;
/*! The userdata string given do exec() api function */
const char *user_data;
/*! The client api call id associated with this event */
int call_id;
/*! The operation's timeout period in ms. */
int timeout;
/*! The operation's recurring interval in ms. */
int interval;
/*! The operation's start delay value in ms. */
int start_delay;
/*! This operation that just completed is on a deleted rsc. */
int rsc_deleted;
/*! The executed ra return code */
enum lrmd_exec_rc rc;
/*! The lrmd status returned for exec_complete events */
int op_status;
/*! stdout from resource agent operation */
const char *output;
/*! Timestamp of when op ran */
unsigned int t_run;
/*! Timestamp of last rc change */
unsigned int t_rcchange;
/*! Time in length op took to execute */
unsigned int exec_time;
/*! Time in length spent in queue */
unsigned int queue_time;
/* This is a GHashTable containing the
* parameters given to the operation */
void *params;
+
+ /* If this is a remote connection, this is the remote node name
+ * registered for the connection. */
+ const char *remote_nodename;
+
} lrmd_event_data_t;
lrmd_event_data_t *lrmd_copy_event(lrmd_event_data_t *event);
void lrmd_free_event(lrmd_event_data_t *event);
typedef struct lrmd_rsc_info_s {
char *id;
char *type;
char *class;
char *provider;
} lrmd_rsc_info_t;
lrmd_rsc_info_t *lrmd_copy_rsc_info(lrmd_rsc_info_t *rsc_info);
void lrmd_free_rsc_info(lrmd_rsc_info_t *rsc_info);
typedef void (*lrmd_event_callback)(lrmd_event_data_t *event);
typedef struct lrmd_list_s {
const char *val;
struct lrmd_list_s *next;
} lrmd_list_t;
void lrmd_list_freeall(lrmd_list_t *head);
typedef struct lrmd_api_operations_s
{
/*!
* \brief Connect from the lrmd.
*
* \retval 0, success
* \retval negative error code on failure
*/
int (*connect) (lrmd_t *lrmd, const char *client_name, int *fd);
/*!
* \brief Disconnect from the lrmd.
*
* \retval 0, success
* \retval negative error code on failure
*/
int (*disconnect)(lrmd_t *lrmd);
/*!
* \brief Register a resource with the lrmd.
*
* \note Synchronous, guaranteed to occur in daemon before function returns.
*
* \retval 0, success
* \retval negative error code on failure
*/
int (*register_rsc) (lrmd_t *lrmd,
const char *rsc_id,
const char *class,
const char *provider,
const char *agent,
enum lrmd_call_options options);
/*!
* \brief Retrieve registration info for a rsc
*
* \retval info on success
* \retval NULL on failure
*/
lrmd_rsc_info_t *(*get_rsc_info) (lrmd_t *lrmd,
const char *rsc_id,
enum lrmd_call_options options);
/*!
* \brief Unregister a resource from the lrmd.
*
* \note All pending and recurring operations will be cancelled
* automatically.
*
* \note Synchronous, guaranteed to occur in daemon before function returns.
*
* \retval 0, success
* \retval -1, success, but operations are currently executing on the rsc which will
* return once they are completed.
* \retval negative error code on failure
*
*/
int (*unregister_rsc) (lrmd_t *lrmd,
const char *rsc_id,
enum lrmd_call_options options);
/*!
* \brief Sets the callback to receive lrmd events on.
*/
void (*set_callback) (lrmd_t *lrmd,
lrmd_event_callback callback);
/*!
* \brief Issue a command on a resource
*
* \note Asynchronous, command is queued in daemon on function return, but
* execution of command is not synced.
*
* \note Operations on individual resources are guaranteed to occur
* in the order the client api calls them in.
*
* \note Operations between different resources are not guaranteed
* to occur in any specific order in relation to one another
* regardless of what order the client api is called in.
* \retval call_id to track async event result on success
* \retval negative error code on failure
*/
int (*exec)(lrmd_t *lrmd,
const char *rsc_id,
const char *action,
const char *userdata, /* userdata string given back in event notification */
int interval, /* ms */
int timeout, /* ms */
int start_delay, /* ms */
enum lrmd_call_options options,
lrmd_key_value_t *params); /* ownership of params is given up to api here */
/*!
* \brief Cancel a recurring command.
*
* \note Synchronous, guaranteed to occur in daemon before function returns.
*
* \note The cancel is completed async from this call.
* We can be guaranteed the cancel has completed once
* the callback receives an exec_complete event with
* the lrmd_op_status signifying that the operation is
* cancelled.
* \note For each resource, cancel operations and exec operations
* are processed in the order they are received.
* It is safe to assume that for a single resource, a cancel
* will occur in the lrmd before an exec if the client's cancel
* api call occurs before the exec api call.
*
* It is not however safe to assume any operation on one resource will
* occur before an operation on another resource regardless of
* the order the client api is called in.
*
* \retval 0, cancel command sent.
* \retval negative error code on failure
*/
int (*cancel)(lrmd_t *lrmd,
const char *rsc_id,
const char *action,
int interval);
/*!
* \brief Get the metadata documentation for a resource.
*
* \note Value is returned in output. Output must be freed when set
*
* \retval lrmd_ok success
* \retval negative error code on failure
*/
int (*get_metadata) (lrmd_t *lrmd,
const char *class,
const char *provider,
const char *agent,
char **output,
enum lrmd_call_options options);
/*!
* \brief Retrieve a list of installed resource agents.
*
* \note if class is not provided, all known agents will be returned
* \note list must be freed using lrmd_list_freeall()
*
* \retval num items in list on success
* \retval negative error code on failure
*/
int (*list_agents)(lrmd_t *lrmd, lrmd_list_t **agents, const char *class, const char *provider);
/*!
* \brief Retrieve a list of resource agent providers
*
* \note When the agent is provided, only the agent's provider will be returned
* \note When no agent is supplied, all providers will be returned.
* \note List must be freed using lrmd_list_freeall()
*
* \retval num items in list on success
* \retval negative error code on failure
*/
int (*list_ocf_providers)(lrmd_t *lrmd,
const char *agent,
lrmd_list_t **providers);
/*!
* \brief Retrieve a list of standards supported by this machine/installation
*
* \note List must be freed using lrmd_list_freeall()
*
* \retval num items in list on success
* \retval negative error code on failure
*/
int (*list_standards)(lrmd_t *lrmd, lrmd_list_t **standards);
} lrmd_api_operations_t;
struct lrmd_s {
lrmd_api_operations_t *cmds;
void *private;
};
static inline const char *
lrmd_event_rc2str(enum lrmd_exec_rc rc)
{
switch(rc) {
case PCMK_EXECRA_OK:
return "ok";
case PCMK_EXECRA_UNKNOWN_ERROR:
return "unknown error";
case PCMK_EXECRA_INVALID_PARAM:
return "invalid parameter";
case PCMK_EXECRA_UNIMPLEMENT_FEATURE:
return "unimplemented feature";
case PCMK_EXECRA_INSUFFICIENT_PRIV:
return "insufficient privileges";
case PCMK_EXECRA_NOT_INSTALLED:
return "not installed";
case PCMK_EXECRA_NOT_CONFIGURED:
return "not configured";
case PCMK_EXECRA_NOT_RUNNING:
return "not running";
case PCMK_EXECRA_RUNNING_MASTER:
return "master";
case PCMK_EXECRA_FAILED_MASTER:
return "master (failed)";
case PCMK_EXECRA_STATUS_UNKNOWN:
return "status: unknown";
default:
break;
}
return "<unknown>";
}
static inline const char *
lrmd_event_type2str(enum lrmd_callback_event type)
{
switch (type) {
case lrmd_event_register:
return "register";
case lrmd_event_unregister:
return "unregister";
case lrmd_event_exec_complete:
return "exec_complete";
case lrmd_event_disconnect:
return "disconnect";
}
return "unknown";
}
#endif
diff --git a/lib/lrmd/lrmd_client.c b/lib/lrmd/lrmd_client.c
index 36c4e6f62e..c9cec65be1 100644
--- a/lib/lrmd/lrmd_client.c
+++ b/lib/lrmd/lrmd_client.c
@@ -1,1650 +1,1661 @@
/*
* Copyright (c) 2012 David Vossel <dvossel@redhat.com>
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include <crm_internal.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <glib.h>
#include <dirent.h>
#include <crm/crm.h>
#include <crm/lrmd.h>
#include <crm/services.h>
#include <crm/common/mainloop.h>
#include <crm/msg_xml.h>
#include <crm/stonith-ng.h>
#ifdef HAVE_GNUTLS_GNUTLS_H
# undef KEYFILE
# include <gnutls/gnutls.h>
#endif
#include <sys/socket.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <netdb.h>
CRM_TRACE_INIT_DATA(lrmd);
static stonith_t *stonith_api = NULL;
static int lrmd_api_disconnect(lrmd_t *lrmd);
static int lrmd_connected(lrmd_t *lrmd);
#ifdef HAVE_GNUTLS_GNUTLS_H
#define LRMD_CLIENT_HANDSHAKE_TIMEOUT 5000 /* 5 seconds */
gnutls_psk_client_credentials_t psk_cred_s;
int lrmd_tls_set_key(gnutls_datum_t *key, const char *location);
static void lrmd_tls_disconnect(lrmd_t *lrmd);
static int global_remote_msg_id = 0;
int lrmd_tls_send_msg(crm_remote_t *session, xmlNode *msg, uint32_t id, const char *msg_type);
static void lrmd_tls_connection_destroy(gpointer userdata);
#endif
typedef struct lrmd_private_s {
enum client_type type;
char *token;
mainloop_io_t *source;
/* IPC parameters */
crm_ipc_t *ipc;
crm_remote_t *remote;
/* Extra TLS parameters */
+ char *remote_nodename;
#ifdef HAVE_GNUTLS_GNUTLS_H
char *server;
int port;
gnutls_psk_client_credentials_t psk_cred_c;
int sock;
GList *pending_notify;
crm_trigger_t *process_notify;
#endif
lrmd_event_callback callback;
} lrmd_private_t;
static lrmd_list_t *
lrmd_list_add(lrmd_list_t * head, const char *value)
{
lrmd_list_t *p, *end;
p = calloc(1, sizeof(lrmd_list_t));
p->val = strdup(value);
end = head;
while (end && end->next) {
end = end->next;
}
if (end) {
end->next = p;
} else {
head = p;
}
return head;
}
void
lrmd_list_freeall(lrmd_list_t * head)
{
lrmd_list_t *p;
while (head) {
char *val = (char *)head->val;
p = head->next;
free(val);
free(head);
head = p;
}
}
lrmd_key_value_t *
lrmd_key_value_add(lrmd_key_value_t * head, const char *key, const char *value)
{
lrmd_key_value_t *p, *end;
p = calloc(1, sizeof(lrmd_key_value_t));
p->key = strdup(key);
p->value = strdup(value);
end = head;
while (end && end->next) {
end = end->next;
}
if (end) {
end->next = p;
} else {
head = p;
}
return head;
}
static void
lrmd_key_value_freeall(lrmd_key_value_t * head)
{
lrmd_key_value_t *p;
while (head) {
p = head->next;
free(head->key);
free(head->value);
free(head);
head = p;
}
}
static void
dup_attr(gpointer key, gpointer value, gpointer user_data)
{
g_hash_table_replace(user_data, strdup(key), strdup(value));
}
lrmd_event_data_t *
lrmd_copy_event(lrmd_event_data_t * event)
{
lrmd_event_data_t *copy = NULL;
copy = calloc(1, sizeof(lrmd_event_data_t));
/* This will get all the int values.
* we just have to be careful not to leave any
* dangling pointers to strings. */
memcpy(copy, event, sizeof(lrmd_event_data_t));
copy->rsc_id = event->rsc_id ? strdup(event->rsc_id) : NULL;
copy->op_type = event->op_type ? strdup(event->op_type) : NULL;
copy->user_data = event->user_data ? strdup(event->user_data) : NULL;
copy->output = event->output ? strdup(event->output) : NULL;
+ copy->remote_nodename = event->remote_nodename ? strdup(event->remote_nodename) : NULL;
if (event->params) {
copy->params = g_hash_table_new_full(crm_str_hash,
g_str_equal, g_hash_destroy_str, g_hash_destroy_str);
if (copy->params != NULL) {
g_hash_table_foreach(event->params, dup_attr, copy->params);
}
}
return copy;
}
void
lrmd_free_event(lrmd_event_data_t * event)
{
if (!event) {
return;
}
/* free gives me grief if i try to cast */
free((char *)event->rsc_id);
free((char *)event->op_type);
free((char *)event->user_data);
free((char *)event->output);
+ free((char *)event->remote_nodename);
if (event->params) {
g_hash_table_destroy(event->params);
}
free(event);
}
static int
lrmd_dispatch_internal(lrmd_t *lrmd, xmlNode *msg)
{
const char *type;
lrmd_private_t *native = lrmd->private;
lrmd_event_data_t event = { 0, };
if (!native->callback) {
/* no callback set */
crm_trace("notify event received but client has not set callback");
return 1;
}
+ event.remote_nodename = native->remote_nodename;
type = crm_element_value(msg, F_LRMD_OPERATION);
crm_element_value_int(msg, F_LRMD_CALLID, &event.call_id);
event.rsc_id = crm_element_value(msg, F_LRMD_RSC_ID);
if (crm_str_eq(type, LRMD_OP_RSC_REG, TRUE)) {
event.type = lrmd_event_register;
} else if (crm_str_eq(type, LRMD_OP_RSC_UNREG, TRUE)) {
event.type = lrmd_event_unregister;
} else if (crm_str_eq(type, LRMD_OP_RSC_EXEC, TRUE)) {
crm_element_value_int(msg, F_LRMD_TIMEOUT, &event.timeout);
crm_element_value_int(msg, F_LRMD_RSC_INTERVAL, &event.interval);
crm_element_value_int(msg, F_LRMD_RSC_START_DELAY, &event.start_delay);
crm_element_value_int(msg, F_LRMD_EXEC_RC, (int *)&event.rc);
crm_element_value_int(msg, F_LRMD_OP_STATUS, &event.op_status);
crm_element_value_int(msg, F_LRMD_RSC_DELETED, &event.rsc_deleted);
crm_element_value_int(msg, F_LRMD_RSC_RUN_TIME, (int *)&event.t_run);
crm_element_value_int(msg, F_LRMD_RSC_RCCHANGE_TIME, (int *)&event.t_rcchange);
crm_element_value_int(msg, F_LRMD_RSC_EXEC_TIME, (int *)&event.exec_time);
crm_element_value_int(msg, F_LRMD_RSC_QUEUE_TIME, (int *)&event.queue_time);
event.op_type = crm_element_value(msg, F_LRMD_RSC_ACTION);
event.user_data = crm_element_value(msg, F_LRMD_RSC_USERDATA_STR);
event.output = crm_element_value(msg, F_LRMD_RSC_OUTPUT);
event.type = lrmd_event_exec_complete;
event.params = xml2list(msg);
} else {
return 1;
}
crm_trace("op %s notify event received", type);
native->callback(&event);
if (event.params) {
g_hash_table_destroy(event.params);
}
return 1;
}
static int
lrmd_ipc_dispatch(const char *buffer, ssize_t length, gpointer userdata)
{
lrmd_t *lrmd = userdata;
lrmd_private_t *native = lrmd->private;
xmlNode *msg;
int rc;
if (!native->callback) {
/* no callback set */
return 1;
}
msg = string2xml(buffer);
rc = lrmd_dispatch_internal(lrmd, msg);
free_xml(msg);
return rc;
}
#ifdef HAVE_GNUTLS_GNUTLS_H
static void
lrmd_free_xml(gpointer userdata)
{
free_xml((xmlNode *) userdata);
}
static int
lrmd_tls_connected(lrmd_t *lrmd)
{
lrmd_private_t *native = lrmd->private;
if (native->remote->tls_session) {
return TRUE;
}
return FALSE;
}
static int
lrmd_tls_dispatch(gpointer userdata)
{
lrmd_t *lrmd = userdata;
lrmd_private_t *native = lrmd->private;
xmlNode *xml = NULL;
int rc = 0;
int disconnected = 0;
if (lrmd_tls_connected(lrmd) == FALSE) {
crm_trace("tls dispatch triggered after disconnect");
return 0;
}
crm_trace("tls_dispatch triggered");
/* First check if there are any pending notifies to process that came
* while we were waiting for replies earlier. */
if (native->pending_notify) {
GList *iter = NULL;
crm_trace("Processing pending notifies");
for (iter = native->pending_notify; iter; iter = iter->next) {
lrmd_dispatch_internal(lrmd, iter->data);
}
g_list_free_full(native->pending_notify, lrmd_free_xml);
native->pending_notify = NULL;
}
/* Next read the current buffer and see if there are any messages to handle. */
rc = crm_remote_ready(native->remote, 0);
if (rc == 0) {
/* nothing to read, see if any full messages are already in buffer. */
xml = crm_remote_parse_buffer(native->remote);
} else if (rc < 0) {
disconnected = 1;
} else {
crm_remote_recv(native->remote, -1, &disconnected);
xml = crm_remote_parse_buffer(native->remote);
}
while (xml) {
lrmd_dispatch_internal(lrmd, xml);
free_xml(xml);
xml = crm_remote_parse_buffer(native->remote);
}
if (disconnected) {
crm_info("Server disconnected while reading remote server msg.");
lrmd_tls_disconnect(lrmd);
return 0;
}
return 1;
}
#endif
/* Not used with mainloop */
int lrmd_poll(lrmd_t *lrmd, int timeout)
{
lrmd_private_t *native = lrmd->private;
switch (native->type) {
case CRM_CLIENT_IPC:
return crm_ipc_ready(native->ipc);
#ifdef HAVE_GNUTLS_GNUTLS_H
case CRM_CLIENT_TLS:
if (native->pending_notify) {
return 1;
} else if (native->remote->buffer
&& strstr(native->remote->buffer, REMOTE_MSG_TERMINATOR)) {
return 1;
}
return crm_remote_ready(native->remote, 0);
#endif
default:
crm_err("Unsupported connection type: %d", native->type);
}
return 0;
}
/* Not used with mainloop */
bool
lrmd_dispatch(lrmd_t * lrmd)
{
lrmd_private_t *private = NULL;
CRM_ASSERT(lrmd != NULL);
private = lrmd->private;
switch (private->type) {
case CRM_CLIENT_IPC:
while (crm_ipc_ready(private->ipc)) {
if (crm_ipc_read(private->ipc) > 0) {
const char *msg = crm_ipc_buffer(private->ipc);
lrmd_ipc_dispatch(msg, strlen(msg), lrmd);
}
}
break;
#ifdef HAVE_GNUTLS_GNUTLS_H
case CRM_CLIENT_TLS:
lrmd_tls_dispatch(lrmd);
break;
#endif
default:
crm_err("Unsupported connection type: %d", private->type);
}
if (lrmd_connected(lrmd) == FALSE) {
crm_err("Connection closed");
return FALSE;
}
return TRUE;
}
static xmlNode *
lrmd_create_op(const char *token, const char *op, xmlNode * data, enum lrmd_call_options options)
{
xmlNode *op_msg = create_xml_node(NULL, "lrmd_command");
CRM_CHECK(op_msg != NULL, return NULL);
CRM_CHECK(token != NULL, return NULL);
crm_xml_add(op_msg, F_XML_TAGNAME, "lrmd_command");
crm_xml_add(op_msg, F_TYPE, T_LRMD);
crm_xml_add(op_msg, F_LRMD_CALLBACK_TOKEN, token);
crm_xml_add(op_msg, F_LRMD_OPERATION, op);
crm_trace("Sending call options: %.8lx, %d", (long)options, options);
crm_xml_add_int(op_msg, F_LRMD_CALLOPTS, options);
if (data != NULL) {
add_message_xml(op_msg, F_LRMD_CALLDATA, data);
}
return op_msg;
}
static void
lrmd_ipc_connection_destroy(gpointer userdata)
{
lrmd_t *lrmd = userdata;
lrmd_private_t *native = lrmd->private;
crm_info("IPC connection destroyed");
/* Prevent these from being cleaned up in lrmd_api_disconnect() */
native->ipc = NULL;
native->source = NULL;
if (native->callback) {
lrmd_event_data_t event = { 0, };
event.type = lrmd_event_disconnect;
native->callback(&event);
}
}
#ifdef HAVE_GNUTLS_GNUTLS_H
static void
lrmd_tls_connection_destroy(gpointer userdata)
{
lrmd_t *lrmd = userdata;
lrmd_private_t *native = lrmd->private;
crm_info("TLS connection destroyed");
if (native->remote->tls_session) {
gnutls_bye(*native->remote->tls_session, GNUTLS_SHUT_RDWR);
gnutls_deinit(*native->remote->tls_session);
gnutls_free(native->remote->tls_session);
}
if (native->psk_cred_c) {
gnutls_psk_free_client_credentials(native->psk_cred_c);
}
if (native->sock) {
close(native->sock);
}
if (native->process_notify) {
mainloop_destroy_trigger(native->process_notify);
native->process_notify = NULL;
}
if (native->pending_notify) {
g_list_free_full(native->pending_notify, lrmd_free_xml);
native->pending_notify = NULL;
}
free(native->remote->buffer);
native->remote->buffer = NULL;
native->source = 0;
native->sock = 0;
native->psk_cred_c = NULL;
native->remote->tls_session = NULL;
native->sock = 0;
if (native->callback) {
lrmd_event_data_t event = { 0, };
+ event.remote_nodename = native->remote_nodename;
event.type = lrmd_event_disconnect;
native->callback(&event);
}
return;
}
int
lrmd_tls_send_msg(crm_remote_t *session, xmlNode *msg, uint32_t id, const char *msg_type)
{
int rc = -1;
crm_xml_add_int(msg, F_LRMD_REMOTE_MSG_ID, id);
crm_xml_add(msg, F_LRMD_REMOTE_MSG_TYPE, msg_type);
rc = crm_remote_send(session, msg);
if (rc < 0) {
crm_err("Failed to send remote lrmd tls msg, rc = %d" , rc);
return rc;
}
return rc;
}
static xmlNode *
lrmd_tls_recv_reply(lrmd_t *lrmd, int total_timeout, int expected_reply_id, int *disconnected)
{
lrmd_private_t *native = lrmd->private;
xmlNode *xml = NULL;
time_t start = time(NULL);
const char *msg_type = NULL;
int reply_id = 0;
int remaining_timeout = 0;
if (total_timeout == 0) {
total_timeout = 10000;
} else if (total_timeout == -1) {
total_timeout = 30000;
}
while (!xml) {
xml = crm_remote_parse_buffer(native->remote);
if (!xml) {
/* read some more off the tls buffer if we still have time left. */
if (remaining_timeout) {
remaining_timeout = remaining_timeout - ((time(NULL) - start) * 1000);
} else {
remaining_timeout = total_timeout;
}
if (remaining_timeout <= 0) {
return NULL;
}
crm_remote_recv(native->remote, remaining_timeout, disconnected);
xml = crm_remote_parse_buffer(native->remote);
if (!xml || *disconnected) {
return NULL;
}
}
CRM_ASSERT(xml != NULL);
crm_element_value_int(xml, F_LRMD_REMOTE_MSG_ID, &reply_id);
msg_type = crm_element_value(xml, F_LRMD_REMOTE_MSG_TYPE);
if (!msg_type) {
crm_err("Empty msg type received while waiting for reply");
free_xml(xml);
xml = NULL;
} else if (safe_str_eq(msg_type, "notify")) {
/* got a notify while waiting for reply, trigger the notify to be processed later */
crm_info("queueing notify");
native->pending_notify = g_list_append(native->pending_notify, xml);
if (native->process_notify) {
crm_info("notify trigger set.");
mainloop_set_trigger(native->process_notify);
}
xml = NULL;
} else if (safe_str_neq(msg_type, "reply")) {
/* msg isn't a reply, make some noise */
crm_err("Expected a reply, got %s", msg_type);
free_xml(xml);
xml = NULL;
} else if (reply_id != expected_reply_id) {
crm_err("Got outdated reply, expected id %d got id %d", expected_reply_id, reply_id);
free_xml(xml);
xml = NULL;
}
}
if (native->remote->buffer && native->process_notify) {
mainloop_set_trigger(native->process_notify);
}
return xml;
}
static int
lrmd_tls_send_recv(lrmd_t *lrmd, xmlNode *msg, int timeout, xmlNode **reply)
{
int rc = 0;
int disconnected = 0;
xmlNode *xml = NULL;
lrmd_private_t *native = lrmd->private;
if (lrmd_tls_connected(lrmd) == FALSE) {
return -1;
}
global_remote_msg_id++;
if (global_remote_msg_id <= 0) {
global_remote_msg_id = 1;
}
rc = lrmd_tls_send_msg(native->remote, msg, global_remote_msg_id, "request");
if (rc <= 0) {
crm_err("Remote lrmd send failed, disconnecting");
lrmd_tls_disconnect(lrmd);
return -ENOTCONN;
}
xml = lrmd_tls_recv_reply(lrmd, timeout, global_remote_msg_id, &disconnected);
if (disconnected) {
crm_err("Remote lrmd server disconnected while waiting for reply with id %d. ", global_remote_msg_id);
lrmd_tls_disconnect(lrmd);
rc = -ENOTCONN;
} else if (!xml) {
crm_err("Remote lrmd never received reply for request id %d. timeout: %dms ", global_remote_msg_id, timeout);
rc = -ECOMM;
}
if (reply) {
*reply = xml;
} else {
free_xml(xml);
}
return rc;
}
#endif
static int
lrmd_send_xml(lrmd_t *lrmd, xmlNode *msg, int timeout, xmlNode **reply)
{
int rc = -1;
lrmd_private_t *native = lrmd->private;
switch (native->type) {
case CRM_CLIENT_IPC:
rc = crm_ipc_send(native->ipc, msg, crm_ipc_client_response, timeout, reply);
break;
#ifdef HAVE_GNUTLS_GNUTLS_H
case CRM_CLIENT_TLS:
rc = lrmd_tls_send_recv(lrmd, msg, timeout, reply);
break;
#endif
default:
crm_err("Unsupported connection type: %d", native->type);
}
return rc;
}
static int
lrmd_connected(lrmd_t *lrmd)
{
lrmd_private_t *native = lrmd->private;
switch (native->type) {
case CRM_CLIENT_IPC:
return crm_ipc_connected(native->ipc);
break;
#ifdef HAVE_GNUTLS_GNUTLS_H
case CRM_CLIENT_TLS:
return lrmd_tls_connected(lrmd);
break;
#endif
default:
crm_err("Unsupported connection type: %d", native->type);
}
return 0;
}
static int
lrmd_send_command(lrmd_t * lrmd, const char *op, xmlNode * data, xmlNode ** output_data, int timeout, /* ms. defaults to 1000 if set to 0 */
enum lrmd_call_options options)
{
int rc = pcmk_ok;
int reply_id = -1;
lrmd_private_t *native = lrmd->private;
xmlNode *op_msg = NULL;
xmlNode *op_reply = NULL;
if (!lrmd_connected(lrmd)) {
return -ENOTCONN;
}
if (op == NULL) {
crm_err("No operation specified");
return -EINVAL;
}
CRM_CHECK(native->token != NULL,;);
crm_trace("sending %s op to lrmd", op);
op_msg = lrmd_create_op(native->token, op, data, options);
if (op_msg == NULL) {
return -EINVAL;
}
crm_xml_add_int(op_msg, F_LRMD_TIMEOUT, timeout);
rc = lrmd_send_xml(lrmd, op_msg, timeout, &op_reply);
free_xml(op_msg);
if (rc < 0) {
crm_perror(LOG_ERR, "Couldn't perform %s operation (timeout=%d): %d", op, timeout, rc);
rc = -ECOMM;
goto done;
}
rc = pcmk_ok;
crm_element_value_int(op_reply, F_LRMD_CALLID, &reply_id);
crm_trace("%s op reply received", op);
if (crm_element_value_int(op_reply, F_LRMD_RC, &rc) != 0) {
rc = -ENOMSG;
goto done;
}
crm_log_xml_trace(op_reply, "Reply");
if (output_data) {
*output_data = op_reply;
op_reply = NULL; /* Prevent subsequent free */
}
done:
if (lrmd_connected(lrmd) == FALSE) {
crm_err("LRMD disconnected");
}
free_xml(op_reply);
return rc;
}
static int
lrmd_handshake(lrmd_t *lrmd, const char *name)
{
int rc = pcmk_ok;
lrmd_private_t *native = lrmd->private;
xmlNode *reply = NULL;
xmlNode *hello = create_xml_node(NULL, "lrmd_command");
crm_xml_add(hello, F_TYPE, T_LRMD);
crm_xml_add(hello, F_LRMD_OPERATION, CRM_OP_REGISTER);
crm_xml_add(hello, F_LRMD_CLIENTNAME, name);
rc = lrmd_send_xml(lrmd, hello, -1, &reply);
if (rc < 0) {
crm_perror(LOG_DEBUG, "Couldn't complete registration with the lrmd API: %d", rc);
rc = -ECOMM;
} else if (reply == NULL) {
crm_err("Did not receive registration reply");
rc = -EPROTO;
} else {
const char *msg_type = crm_element_value(reply, F_LRMD_OPERATION);
const char *tmp_ticket = crm_element_value(reply, F_LRMD_CLIENTID);
if (safe_str_neq(msg_type, CRM_OP_REGISTER)) {
crm_err("Invalid registration message: %s", msg_type);
crm_log_xml_err(reply, "Bad reply");
rc = -EPROTO;
} else if (tmp_ticket == NULL) {
crm_err("No registration token provided");
crm_log_xml_err(reply, "Bad reply");
rc = -EPROTO;
} else {
crm_trace("Obtained registration token: %s", tmp_ticket);
native->token = strdup(tmp_ticket);
rc = pcmk_ok;
}
}
free_xml(reply);
free_xml(hello);
return rc;
}
static int
lrmd_ipc_connect(lrmd_t * lrmd, int *fd)
{
int rc = pcmk_ok;
lrmd_private_t *native = lrmd->private;
static struct ipc_client_callbacks lrmd_callbacks = {
.dispatch = lrmd_ipc_dispatch,
.destroy = lrmd_ipc_connection_destroy
};
crm_info("Connecting to lrmd");
if (fd) {
/* No mainloop */
native->ipc = crm_ipc_new("lrmd", 0);
if (native->ipc && crm_ipc_connect(native->ipc)) {
*fd = crm_ipc_get_fd(native->ipc);
} else if (native->ipc) {
rc = -ENOTCONN;
}
} else {
native->source = mainloop_add_ipc_client("lrmd", G_PRIORITY_HIGH, 0, lrmd, &lrmd_callbacks);
native->ipc = mainloop_get_ipc_client(native->source);
}
if (native->ipc == NULL) {
crm_debug("Could not connect to the LRMD API");
rc = -ENOTCONN;
}
return rc;
}
#ifdef HAVE_GNUTLS_GNUTLS_H
int lrmd_tls_set_key(gnutls_datum_t *key, const char *location)
{
FILE *stream;
int read_len = 256;
int cur_len = 0;
int buf_len = read_len;
static char *key_cache = NULL;
static size_t key_cache_len = 0;
static time_t key_cache_updated;
if (key_cache) {
time_t now = time(NULL);
if ((now - key_cache_updated) < 60) {
key->data = gnutls_malloc(key_cache_len + 1);
key->size = key_cache_len;
memcpy(key->data, key_cache, key_cache_len);
crm_debug("using cached LRMD key");
return 0;
} else {
key_cache_len = 0;
key_cache_updated = 0;
free(key_cache);
key_cache = NULL;
crm_debug("clearing lrmd key cache");
}
}
stream = fopen(location, "r");
if (!stream) {
return -1;
}
key->data = gnutls_malloc(read_len);
while (!feof(stream)) {
char next;
if (cur_len == buf_len) {
buf_len = cur_len + read_len;
key->data = gnutls_realloc(key->data, buf_len);
}
next = fgetc(stream);
if (next == EOF && feof(stream)) {
break;
}
key->data[cur_len] = next;
cur_len++;
}
fclose(stream);
key->size = cur_len;
if (!cur_len) {
gnutls_free(key->data);
key->data = 0;
return -1;
}
if (!key_cache) {
key_cache = calloc(1, key->size+1);
memcpy(key_cache, key->data, key->size);
key_cache_len = key->size;
key_cache_updated = time(NULL);
}
return 0;
}
static int
lrmd_tls_key_cb(gnutls_session_t session, char **username, gnutls_datum_t *key)
{
int rc = 0;
if (lrmd_tls_set_key(key, DEFAULT_REMOTE_KEY_LOCATION)) {
rc = lrmd_tls_set_key(key, ALT_REMOTE_KEY_LOCATION);
}
if (rc) {
crm_err("No lrmd remote key found");
return -1;
}
*username = gnutls_malloc(strlen(DEFAULT_REMOTE_USERNAME) + 1);
strcpy(*username, DEFAULT_REMOTE_USERNAME);
return rc;
}
#endif
static int
lrmd_tls_connect(lrmd_t *lrmd, int *fd)
{
#ifdef HAVE_GNUTLS_GNUTLS_H
static struct mainloop_fd_callbacks lrmd_tls_callbacks =
{
.dispatch = lrmd_tls_dispatch,
.destroy = lrmd_tls_connection_destroy,
};
lrmd_private_t *native = lrmd->private;
static int gnutls_init = 0;
int sock;
if (!gnutls_init) {
gnutls_global_init();
}
gnutls_psk_allocate_client_credentials(&native->psk_cred_c);
gnutls_psk_set_client_credentials_function(native->psk_cred_c, lrmd_tls_key_cb);
sock = crm_remote_tcp_connect(native->server, native->port);
if (sock <= 0) {
crm_warn("Could not establish remote lrmd connection to %s", native->server);
lrmd_tls_connection_destroy(lrmd);
return -ENOTCONN;
}
native->sock = sock;
native->remote->tls_session = create_psk_tls_session(sock, GNUTLS_CLIENT, native->psk_cred_c);
if (crm_initiate_client_tls_handshake(native->remote, LRMD_CLIENT_HANDSHAKE_TIMEOUT) != 0) {
crm_err("Session creation for %s:%d failed", native->server, native->port);
gnutls_deinit(*native->remote->tls_session);
gnutls_free(native->remote->tls_session);
native->remote->tls_session = NULL;
lrmd_tls_connection_destroy(lrmd);
return -1;
}
crm_info("Remote lrmd client TLS connection established with server %s:%d", native->server, native->port);
if (fd) {
*fd = sock;
} else {
char name[256] = { 0, };
snprintf(name, 128, "remote-lrmd-%s:%d", native->server, native->port);
native->process_notify = mainloop_add_trigger(G_PRIORITY_HIGH, lrmd_tls_dispatch, lrmd);
native->source = mainloop_add_fd(name, G_PRIORITY_HIGH, native->sock, lrmd, &lrmd_tls_callbacks);
}
return pcmk_ok;
#else
crm_err("TLS not enabled for this build.");
return -ENOTCONN;
#endif
}
static int
lrmd_api_connect(lrmd_t * lrmd, const char *name, int *fd)
{
int rc = -ENOTCONN;
lrmd_private_t *native = lrmd->private;
switch (native->type) {
case CRM_CLIENT_IPC:
rc = lrmd_ipc_connect(lrmd, fd);
break;
#ifdef HAVE_GNUTLS_GNUTLS_H
case CRM_CLIENT_TLS:
rc = lrmd_tls_connect(lrmd, fd);
break;
#endif
default:
crm_err("Unsupported connection type: %d", native->type);
}
if (rc == pcmk_ok) {
rc = lrmd_handshake(lrmd, name);
}
if (rc != pcmk_ok) {
lrmd_api_disconnect(lrmd);
}
return rc;
}
static void
lrmd_ipc_disconnect(lrmd_t *lrmd)
{
lrmd_private_t *native = lrmd->private;
if (native->source != NULL) {
/* Attached to mainloop */
mainloop_del_ipc_client(native->source);
native->source = NULL;
native->ipc = NULL;
} else if(native->ipc) {
/* Not attached to mainloop */
crm_ipc_t *ipc = native->ipc;
native->ipc = NULL;
crm_ipc_close(ipc);
crm_ipc_destroy(ipc);
}
}
#ifdef HAVE_GNUTLS_GNUTLS_H
static void
lrmd_tls_disconnect(lrmd_t *lrmd)
{
lrmd_private_t *native = lrmd->private;
if (native->remote->tls_session) {
gnutls_bye(*native->remote->tls_session, GNUTLS_SHUT_RDWR);
gnutls_deinit(*native->remote->tls_session);
gnutls_free(native->remote->tls_session);
native->remote->tls_session = 0;
}
if (native->source != NULL) {
/* Attached to mainloop */
mainloop_del_ipc_client(native->source);
native->source = NULL;
} else if(native->sock) {
close(native->sock);
}
if (native->pending_notify) {
g_list_free_full(native->pending_notify, lrmd_free_xml);
native->pending_notify = NULL;
}
}
#endif
static int
lrmd_api_disconnect(lrmd_t *lrmd)
{
lrmd_private_t *native = lrmd->private;
crm_info("Disconnecting from lrmd service");
switch (native->type) {
case CRM_CLIENT_IPC:
lrmd_ipc_disconnect(lrmd);
break;
#ifdef HAVE_GNUTLS_GNUTLS_H
case CRM_CLIENT_TLS:
lrmd_tls_disconnect(lrmd);
break;
#endif
default:
crm_err("Unsupported connection type: %d", native->type);
}
free(native->token);
native->token = NULL;
return 0;
}
static int
lrmd_api_register_rsc(lrmd_t * lrmd,
const char *rsc_id,
const char *class,
const char *provider, const char *type, enum lrmd_call_options options)
{
int rc = pcmk_ok;
xmlNode *data = NULL;
if (!class || !type || !rsc_id) {
return -EINVAL;
}
if (safe_str_eq(class, "ocf") && !provider) {
return -EINVAL;
}
data = create_xml_node(NULL, F_LRMD_RSC);
crm_xml_add(data, F_LRMD_ORIGIN, __FUNCTION__);
crm_xml_add(data, F_LRMD_RSC_ID, rsc_id);
crm_xml_add(data, F_LRMD_CLASS, class);
crm_xml_add(data, F_LRMD_PROVIDER, provider);
crm_xml_add(data, F_LRMD_TYPE, type);
rc = lrmd_send_command(lrmd, LRMD_OP_RSC_REG, data, NULL, 0, options);
free_xml(data);
return rc;
}
static int
lrmd_api_unregister_rsc(lrmd_t * lrmd, const char *rsc_id, enum lrmd_call_options options)
{
int rc = pcmk_ok;
xmlNode *data = create_xml_node(NULL, F_LRMD_RSC);
crm_xml_add(data, F_LRMD_ORIGIN, __FUNCTION__);
crm_xml_add(data, F_LRMD_RSC_ID, rsc_id);
rc = lrmd_send_command(lrmd, LRMD_OP_RSC_UNREG, data, NULL, 0, options);
free_xml(data);
return rc;
}
lrmd_rsc_info_t *
lrmd_copy_rsc_info(lrmd_rsc_info_t * rsc_info)
{
lrmd_rsc_info_t *copy = NULL;
copy = calloc(1, sizeof(lrmd_rsc_info_t));
copy->id = strdup(rsc_info->id);
copy->type = strdup(rsc_info->type);
copy->class = strdup(rsc_info->class);
if (rsc_info->provider) {
copy->provider = strdup(rsc_info->provider);
}
return copy;
}
void
lrmd_free_rsc_info(lrmd_rsc_info_t * rsc_info)
{
if (!rsc_info) {
return;
}
free(rsc_info->id);
free(rsc_info->type);
free(rsc_info->class);
free(rsc_info->provider);
free(rsc_info);
}
static lrmd_rsc_info_t *
lrmd_api_get_rsc_info(lrmd_t * lrmd, const char *rsc_id, enum lrmd_call_options options)
{
lrmd_rsc_info_t *rsc_info = NULL;
xmlNode *data = create_xml_node(NULL, F_LRMD_RSC);
xmlNode *output = NULL;
const char *class = NULL;
const char *provider = NULL;
const char *type = NULL;
crm_xml_add(data, F_LRMD_ORIGIN, __FUNCTION__);
crm_xml_add(data, F_LRMD_RSC_ID, rsc_id);
lrmd_send_command(lrmd, LRMD_OP_RSC_INFO, data, &output, 0, options);
free_xml(data);
if (!output) {
return NULL;
}
class = crm_element_value(output, F_LRMD_CLASS);
provider = crm_element_value(output, F_LRMD_PROVIDER);
type = crm_element_value(output, F_LRMD_TYPE);
if (!class || !type) {
free_xml(output);
return NULL;
} else if (safe_str_eq(class, "ocf") && !provider) {
free_xml(output);
return NULL;
}
rsc_info = calloc(1, sizeof(lrmd_rsc_info_t));
rsc_info->id = strdup(rsc_id);
rsc_info->class = strdup(class);
if (provider) {
rsc_info->provider = strdup(provider);
}
rsc_info->type = strdup(type);
free_xml(output);
return rsc_info;
}
static void
lrmd_api_set_callback(lrmd_t * lrmd, lrmd_event_callback callback)
{
lrmd_private_t *native = lrmd->private;
native->callback = callback;
}
static int
stonith_get_metadata(const char *provider, const char *type, char **output)
{
int rc = pcmk_ok;
stonith_api->cmds->metadata(stonith_api, st_opt_sync_call, type, provider, output, 0);
if (*output == NULL) {
rc = -EIO;
}
return rc;
}
static int
lsb_get_metadata(const char *type, char **output)
{
#define lsb_metadata_template \
"<?xml version=\"1.0\"?>\n"\
"<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n"\
"<resource-agent name=\"%s\" version=\"0.1\">\n"\
" <version>1.0</version>\n"\
" <longdesc lang=\"en\">\n"\
" %s"\
" </longdesc>\n"\
" <shortdesc lang=\"en\">%s</shortdesc>\n"\
" <parameters>\n"\
" </parameters>\n"\
" <actions>\n"\
" <action name=\"start\" timeout=\"15\" />\n"\
" <action name=\"stop\" timeout=\"15\" />\n"\
" <action name=\"status\" timeout=\"15\" />\n"\
" <action name=\"restart\" timeout=\"15\" />\n"\
" <action name=\"force-reload\" timeout=\"15\" />\n"\
" <action name=\"monitor\" timeout=\"15\" interval=\"15\" />\n"\
" <action name=\"meta-data\" timeout=\"5\" />\n"\
" </actions>\n"\
" <special tag=\"LSB\">\n"\
" <Provides>%s</Provides>\n"\
" <Required-Start>%s</Required-Start>\n"\
" <Required-Stop>%s</Required-Stop>\n"\
" <Should-Start>%s</Should-Start>\n"\
" <Should-Stop>%s</Should-Stop>\n"\
" <Default-Start>%s</Default-Start>\n"\
" <Default-Stop>%s</Default-Stop>\n"\
" </special>\n"\
"</resource-agent>\n"
#define LSB_INITSCRIPT_INFOBEGIN_TAG "### BEGIN INIT INFO"
#define LSB_INITSCRIPT_INFOEND_TAG "### END INIT INFO"
#define PROVIDES "# Provides:"
#define REQ_START "# Required-Start:"
#define REQ_STOP "# Required-Stop:"
#define SHLD_START "# Should-Start:"
#define SHLD_STOP "# Should-Stop:"
#define DFLT_START "# Default-Start:"
#define DFLT_STOP "# Default-Stop:"
#define SHORT_DSCR "# Short-Description:"
#define DESCRIPTION "# Description:"
#define lsb_meta_helper_free_value(m) \
if ((m) != NULL) { \
xmlFree(m); \
(m) = NULL; \
}
#define lsb_meta_helper_get_value(buffer, ptr, keyword) \
if (!ptr && !strncasecmp(buffer, keyword, strlen(keyword))) { \
(ptr) = (char *)xmlEncodeEntitiesReentrant(NULL, BAD_CAST buffer+strlen(keyword)); \
continue; \
}
char ra_pathname[PATH_MAX] = { 0, };
FILE *fp;
GString *meta_data = NULL;
char buffer[1024];
char *provides = NULL;
char *req_start = NULL;
char *req_stop = NULL;
char *shld_start = NULL;
char *shld_stop = NULL;
char *dflt_start = NULL;
char *dflt_stop = NULL;
char *s_dscrpt = NULL;
char *xml_l_dscrpt = NULL;
GString *l_dscrpt = NULL;
snprintf(ra_pathname, sizeof(ra_pathname), "%s%s%s",
type[0] == '/' ? "" : LSB_ROOT_DIR, type[0] == '/' ? "" : "/", type);
if (!(fp = fopen(ra_pathname, "r"))) {
return -EIO;
}
/* Enter into the lsb-compliant comment block */
while (fgets(buffer, sizeof(buffer), fp)) {
/* Now suppose each of the following eight arguments contain only one line */
lsb_meta_helper_get_value(buffer, provides, PROVIDES)
lsb_meta_helper_get_value(buffer, req_start, REQ_START)
lsb_meta_helper_get_value(buffer, req_stop, REQ_STOP)
lsb_meta_helper_get_value(buffer, shld_start, SHLD_START)
lsb_meta_helper_get_value(buffer, shld_stop, SHLD_STOP)
lsb_meta_helper_get_value(buffer, dflt_start, DFLT_START)
lsb_meta_helper_get_value(buffer, dflt_stop, DFLT_STOP)
lsb_meta_helper_get_value(buffer, s_dscrpt, SHORT_DSCR)
/* Long description may cross multiple lines */
if ((l_dscrpt == NULL) && (0 == strncasecmp(buffer, DESCRIPTION, strlen(DESCRIPTION)))) {
l_dscrpt = g_string_new(buffer + strlen(DESCRIPTION));
/* Between # and keyword, more than one space, or a tab character,
* indicates the continuation line. Extracted from LSB init script standard */
while (fgets(buffer, sizeof(buffer), fp)) {
if (!strncmp(buffer, "# ", 3) || !strncmp(buffer, "#\t", 2)) {
buffer[0] = ' ';
l_dscrpt = g_string_append(l_dscrpt, buffer);
} else {
fputs(buffer, fp);
break; /* Long description ends */
}
}
continue;
}
if (l_dscrpt) {
xml_l_dscrpt = (char *)xmlEncodeEntitiesReentrant(NULL, BAD_CAST(l_dscrpt->str));
}
if (!strncasecmp(buffer, LSB_INITSCRIPT_INFOEND_TAG, strlen(LSB_INITSCRIPT_INFOEND_TAG))) {
/* Get to the out border of LSB comment block */
break;
}
if (buffer[0] != '#') {
break; /* Out of comment block in the beginning */
}
}
fclose(fp);
meta_data = g_string_new("");
g_string_sprintf(meta_data, lsb_metadata_template, type,
(xml_l_dscrpt == NULL) ? type : xml_l_dscrpt,
(s_dscrpt == NULL) ? type : s_dscrpt, (provides == NULL) ? "" : provides,
(req_start == NULL) ? "" : req_start, (req_stop == NULL) ? "" : req_stop,
(shld_start == NULL) ? "" : shld_start, (shld_stop == NULL) ? "" : shld_stop,
(dflt_start == NULL) ? "" : dflt_start, (dflt_stop == NULL) ? "" : dflt_stop);
lsb_meta_helper_free_value(xml_l_dscrpt);
lsb_meta_helper_free_value(s_dscrpt);
lsb_meta_helper_free_value(provides);
lsb_meta_helper_free_value(req_start);
lsb_meta_helper_free_value(req_stop);
lsb_meta_helper_free_value(shld_start);
lsb_meta_helper_free_value(shld_stop);
lsb_meta_helper_free_value(dflt_start);
lsb_meta_helper_free_value(dflt_stop);
if (l_dscrpt) {
g_string_free(l_dscrpt, TRUE);
}
*output = strdup(meta_data->str);
g_string_free(meta_data, TRUE);
return pcmk_ok;
}
static int
generic_get_metadata(const char *standard, const char *provider, const char *type, char **output)
{
svc_action_t *action = resources_action_create(type,
standard,
provider,
type,
"meta-data",
0,
5000,
NULL);
if (!(services_action_sync(action))) {
crm_err("Failed to retrieve meta-data for %s:%s:%s", standard, provider, type);
services_action_free(action);
return -EIO;
}
if (!action->stdout_data) {
crm_err("Failed to retrieve meta-data for %s:%s:%s", standard, provider, type);
services_action_free(action);
return -EIO;
}
*output = strdup(action->stdout_data);
services_action_free(action);
return pcmk_ok;
}
static int
lrmd_api_get_metadata(lrmd_t * lrmd,
const char *class,
const char *provider,
const char *type, char **output, enum lrmd_call_options options)
{
if (!class || !type) {
return -EINVAL;
}
if (safe_str_eq(class, "stonith")) {
return stonith_get_metadata(provider, type, output);
} else if (safe_str_eq(class, "lsb")) {
return lsb_get_metadata(type, output);
}
return generic_get_metadata(class, provider, type, output);
}
static int
lrmd_api_exec(lrmd_t * lrmd, const char *rsc_id, const char *action, const char *userdata, int interval, /* ms */
int timeout, /* ms */
int start_delay, /* ms */
enum lrmd_call_options options, lrmd_key_value_t * params)
{
int rc = pcmk_ok;
xmlNode *data = create_xml_node(NULL, F_LRMD_RSC);
xmlNode *args = create_xml_node(data, XML_TAG_ATTRS);
lrmd_key_value_t *tmp = NULL;
crm_xml_add(data, F_LRMD_ORIGIN, __FUNCTION__);
crm_xml_add(data, F_LRMD_RSC_ID, rsc_id);
crm_xml_add(data, F_LRMD_RSC_ACTION, action);
crm_xml_add(data, F_LRMD_RSC_USERDATA_STR, userdata);
crm_xml_add_int(data, F_LRMD_RSC_INTERVAL, interval);
crm_xml_add_int(data, F_LRMD_TIMEOUT, timeout);
crm_xml_add_int(data, F_LRMD_RSC_START_DELAY, start_delay);
for (tmp = params; tmp; tmp = tmp->next) {
hash2field((gpointer) tmp->key, (gpointer) tmp->value, args);
}
rc = lrmd_send_command(lrmd, LRMD_OP_RSC_EXEC, data, NULL, timeout, options);
free_xml(data);
lrmd_key_value_freeall(params);
return rc;
}
static int
lrmd_api_cancel(lrmd_t * lrmd, const char *rsc_id, const char *action, int interval)
{
int rc = pcmk_ok;
xmlNode *data = create_xml_node(NULL, F_LRMD_RSC);
crm_xml_add(data, F_LRMD_ORIGIN, __FUNCTION__);
crm_xml_add(data, F_LRMD_RSC_ACTION, action);
crm_xml_add(data, F_LRMD_RSC_ID, rsc_id);
crm_xml_add_int(data, F_LRMD_RSC_INTERVAL, interval);
rc = lrmd_send_command(lrmd, LRMD_OP_RSC_CANCEL, data, NULL, 0, 0);
free_xml(data);
return rc;
}
static int
list_stonith_agents(lrmd_list_t ** resources)
{
int rc = 0;
stonith_key_value_t *stonith_resources = NULL;
stonith_key_value_t *dIter = NULL;
stonith_api->cmds->list_agents(stonith_api, st_opt_sync_call, NULL, &stonith_resources, 0);
for (dIter = stonith_resources; dIter; dIter = dIter->next) {
rc++;
if(resources) {
*resources = lrmd_list_add(*resources, dIter->value);
}
}
stonith_key_value_freeall(stonith_resources, 1, 0);
return rc;
}
static int
lrmd_api_list_agents(lrmd_t * lrmd, lrmd_list_t ** resources, const char *class,
const char *provider)
{
int rc = 0;
if (safe_str_eq(class, "stonith")) {
rc += list_stonith_agents(resources);
} else {
GListPtr gIter = NULL;
GList *agents = resources_list_agents(class, provider);
for (gIter = agents; gIter != NULL; gIter = gIter->next) {
*resources = lrmd_list_add(*resources, (const char *)gIter->data);
rc++;
}
g_list_free_full(agents, free);
if (!class) {
rc += list_stonith_agents(resources);
}
}
if(rc == 0) {
crm_notice("No agents found for class %s", class);
rc = -EPROTONOSUPPORT;
}
return rc;
}
static int
does_provider_have_agent(const char *agent, const char *provider, const char *class)
{
int found = 0;
GList *agents = NULL;
GListPtr gIter2 = NULL;
agents = resources_list_agents(class, provider);
for (gIter2 = agents; gIter2 != NULL; gIter2 = gIter2->next) {
if (safe_str_eq(agent, gIter2->data)) {
found = 1;
}
}
g_list_free_full(agents, free);
return found;
}
static int
lrmd_api_list_ocf_providers(lrmd_t * lrmd, const char *agent, lrmd_list_t ** providers)
{
int rc = pcmk_ok;
char *provider = NULL;
GList *ocf_providers = NULL;
GListPtr gIter = NULL;
ocf_providers = resources_list_providers("ocf");
for (gIter = ocf_providers; gIter != NULL; gIter = gIter->next) {
provider = gIter->data;
if (!agent || does_provider_have_agent(agent, provider, "ocf")) {
*providers = lrmd_list_add(*providers, (const char *)gIter->data);
rc++;
}
}
g_list_free_full(ocf_providers, free);
return rc;
}
static int
lrmd_api_list_standards(lrmd_t * lrmd, lrmd_list_t ** supported)
{
int rc = 0;
GList *standards = NULL;
GListPtr gIter = NULL;
standards = resources_list_standards();
for (gIter = standards; gIter != NULL; gIter = gIter->next) {
*supported = lrmd_list_add(*supported, (const char *)gIter->data);
rc++;
}
if(list_stonith_agents(NULL) > 0) {
*supported = lrmd_list_add(*supported, "stonith");
rc++;
}
g_list_free_full(standards, free);
return rc;
}
lrmd_t *
lrmd_api_new(void)
{
lrmd_t *new_lrmd = NULL;
lrmd_private_t *pvt = NULL;
new_lrmd = calloc(1, sizeof(lrmd_t));
pvt = calloc(1, sizeof(lrmd_private_t));
pvt->remote = calloc(1, sizeof(crm_remote_t));
new_lrmd->cmds = calloc(1, sizeof(lrmd_api_operations_t));
pvt->type = CRM_CLIENT_IPC;
new_lrmd->private = pvt;
new_lrmd->cmds->connect = lrmd_api_connect;
new_lrmd->cmds->disconnect = lrmd_api_disconnect;
new_lrmd->cmds->register_rsc = lrmd_api_register_rsc;
new_lrmd->cmds->unregister_rsc = lrmd_api_unregister_rsc;
new_lrmd->cmds->get_rsc_info = lrmd_api_get_rsc_info;
new_lrmd->cmds->set_callback = lrmd_api_set_callback;
new_lrmd->cmds->get_metadata = lrmd_api_get_metadata;
new_lrmd->cmds->exec = lrmd_api_exec;
new_lrmd->cmds->cancel = lrmd_api_cancel;
new_lrmd->cmds->list_agents = lrmd_api_list_agents;
new_lrmd->cmds->list_ocf_providers = lrmd_api_list_ocf_providers;
new_lrmd->cmds->list_standards = lrmd_api_list_standards;
if (!stonith_api) {
stonith_api = stonith_api_new();
}
return new_lrmd;
}
lrmd_t *
-lrmd_remote_api_new(const char *server, int port)
+lrmd_remote_api_new(const char *nodename, const char *server, int port)
{
#ifdef HAVE_GNUTLS_GNUTLS_H
lrmd_t *new_lrmd = lrmd_api_new();
lrmd_private_t *native = new_lrmd->private;
+ if (!nodename && !server) {
+ return NULL;
+ }
+
native->type = CRM_CLIENT_TLS;
- native->server = strdup(server);
+ native->remote_nodename = nodename ? strdup(nodename) : strdup(server);
+ native->server = server ? strdup(server) : strdup(nodename);
native->port = port ? port : DEFAULT_REMOTE_PORT;
return new_lrmd;
#else
crm_err("GNUTLS is not enabled for this build, remote LRMD client can not be created");
return NULL;
#endif
}
void
lrmd_api_delete(lrmd_t * lrmd)
{
if (!lrmd) {
return;
}
lrmd->cmds->disconnect(lrmd); /* no-op if already disconnected */
free(lrmd->cmds);
if (lrmd->private) {
lrmd_private_t *native = lrmd->private;
#ifdef HAVE_GNUTLS_GNUTLS_H
free(native->server);
#endif
+ free(native->remote_nodename);
free(native->remote);
}
free(lrmd->private);
free(lrmd);
}
diff --git a/lrmd/test.c b/lrmd/test.c
index 801058dca3..f720ade6a3 100644
--- a/lrmd/test.c
+++ b/lrmd/test.c
@@ -1,590 +1,590 @@
/*
* Copyright (c) 2012 David Vossel <dvossel@redhat.com>
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include <crm_internal.h>
#include <glib.h>
#include <unistd.h>
#include <crm/crm.h>
#include <crm/services.h>
#include <crm/common/mainloop.h>
#include <crm/pengine/status.h>
#include <crm/cib.h>
#include <crm/lrmd.h>
/* *INDENT-OFF* */
static struct crm_option long_options[] = {
{"help", 0, 0, '?'},
{"verbose", 0, 0, 'V', "\t\tPrint out logs and events to screen"},
{"quiet", 0, 0, 'Q', "\t\tSuppress all output to screen"},
{"tls", 0, 0, 'S', "\t\tUse tls backend for local connection"},
{"listen", 1, 0, 'l', "\tListen for a specific event string"},
{"api-call", 1, 0, 'c', "\tDirectly relates to lrmd api functions"},
{"no-wait", 0, 0, 'w', "\tMake api call and do not wait for result."},
{"is-running", 0, 0, 'R', "\tDetermine if a resource is registered and running."},
{"notify-orig", 0, 0, 'n', "\tOnly notify this client the results of an api action."},
{"notify-changes", 0, 0, 'o', "\tOnly notify client changes to recurring operations."},
{"-spacer-", 1, 0, '-', "\nParameters for api-call option"},
{"action", 1, 0, 'a'},
{"rsc-id", 1, 0, 'r'},
{"cancel-call-id", 1, 0, 'x'},
{"provider", 1, 0, 'P'},
{"class", 1, 0, 'C'},
{"type", 1, 0, 'T'},
{"interval", 1, 0, 'i'},
{"timeout", 1, 0, 't'},
{"start-delay", 1, 0, 's'},
{"param-key", 1, 0, 'k'},
{"param-val", 1, 0, 'v'},
{"-spacer-", 1, 0, '-'},
{0, 0, 0, 0}
};
/* *INDENT-ON* */
cib_t *cib_conn = NULL;
static int exec_call_id = 0;
static int exec_call_opts = 0;
extern void cleanup_alloc_calculations(pe_working_set_t * data_set);
static struct {
int verbose;
int quiet;
int print;
int interval;
int timeout;
int start_delay;
int cancel_call_id;
int no_wait;
int is_running;
int no_connect;
const char *api_call;
const char *rsc_id;
const char *provider;
const char *class;
const char *type;
const char *action;
const char *listen;
lrmd_key_value_t *params;
} options;
GMainLoop *mainloop = NULL;
lrmd_t *lrmd_conn = NULL;
static char event_buf_v0[1024];
static void
test_exit(int rc)
{
lrmd_api_delete(lrmd_conn);
crm_exit(rc);
}
#define print_result(result) \
if (!options.quiet) { \
result; \
} \
#define report_event(event) \
snprintf(event_buf_v0, sizeof(event_buf_v0), "NEW_EVENT event_type:%s rsc_id:%s action:%s rc:%s op_status:%s", \
lrmd_event_type2str(event->type), \
event->rsc_id, \
event->op_type ? event->op_type : "none", \
lrmd_event_rc2str(event->rc), \
services_lrm_status_str(event->op_status)); \
crm_info("%s", event_buf_v0);;
static void
test_shutdown(int nsig)
{
lrmd_api_delete(lrmd_conn);
lrmd_conn = NULL;
}
static void
read_events(lrmd_event_data_t * event)
{
report_event(event);
if (options.listen) {
if (safe_str_eq(options.listen, event_buf_v0)) {
print_result(printf("LISTEN EVENT SUCCESSFUL\n"));
test_exit(0);
}
}
if (exec_call_id && (event->call_id == exec_call_id)) {
if (event->op_status == 0 && event->rc == 0) {
print_result(printf("API-CALL SUCCESSFUL for 'exec'\n"));
} else {
print_result(printf("API-CALL FAILURE for 'exec', rc:%d lrmd_op_status:%s\n",
event->rc, services_lrm_status_str(event->op_status)));
test_exit(-1);
}
if (!options.listen) {
test_exit(0);
}
}
}
static gboolean
timeout_err(gpointer data)
{
print_result(printf("LISTEN EVENT FAILURE - timeout occurred, never found.\n"));
test_exit(-1);
return FALSE;
}
static void
try_connect(void)
{
int tries = 10;
int i = 0;
int rc = 0;
for (i = 0; i < tries; i++) {
rc = lrmd_conn->cmds->connect(lrmd_conn, "lrmd", NULL);
if (!rc) {
crm_info("lrmd client connection established");
return;
} else {
crm_info("lrmd client connection failed");
}
sleep(1);
}
print_result(printf("API CONNECTION FAILURE\n"));
test_exit(-1);
}
static gboolean
start_test(gpointer user_data)
{
int rc = 0;
if (!options.no_connect) {
try_connect();
}
lrmd_conn->cmds->set_callback(lrmd_conn, read_events);
if (options.timeout) {
g_timeout_add(options.timeout, timeout_err, NULL);
}
if (!options.api_call) {
return 0;
}
if (safe_str_eq(options.api_call, "exec")) {
rc = lrmd_conn->cmds->exec(lrmd_conn,
options.rsc_id,
options.action,
NULL,
options.interval,
options.timeout,
options.start_delay, exec_call_opts, options.params);
if (rc > 0) {
exec_call_id = rc;
print_result(printf("API-CALL 'exec' action pending, waiting on response\n"));
}
} else if (safe_str_eq(options.api_call, "register_rsc")) {
rc = lrmd_conn->cmds->register_rsc(lrmd_conn,
options.rsc_id,
options.class, options.provider, options.type, 0);
} else if (safe_str_eq(options.api_call, "get_rsc_info")) {
lrmd_rsc_info_t *rsc_info;
rsc_info = lrmd_conn->cmds->get_rsc_info(lrmd_conn, options.rsc_id, 0);
if (rsc_info) {
print_result(printf("RSC_INFO: id:%s class:%s provider:%s type:%s\n",
rsc_info->id, rsc_info->class,
rsc_info->provider ? rsc_info->provider : "<none>",
rsc_info->type));
lrmd_free_rsc_info(rsc_info);
rc = pcmk_ok;
} else {
rc = -1;
}
} else if (safe_str_eq(options.api_call, "unregister_rsc")) {
rc = lrmd_conn->cmds->unregister_rsc(lrmd_conn, options.rsc_id, 0);
} else if (safe_str_eq(options.api_call, "cancel")) {
rc = lrmd_conn->cmds->cancel(lrmd_conn, options.rsc_id, options.action, options.interval);
} else if (safe_str_eq(options.api_call, "metadata")) {
char *output = NULL;
rc = lrmd_conn->cmds->get_metadata(lrmd_conn,
options.class,
options.provider, options.type, &output, 0);
if (rc == pcmk_ok) {
print_result(printf("%s", output));
free(output);
}
} else if (safe_str_eq(options.api_call, "list_agents")) {
lrmd_list_t *list = NULL;
lrmd_list_t *iter = NULL;
rc = lrmd_conn->cmds->list_agents(lrmd_conn, &list, options.class, options.provider);
if (rc > 0) {
print_result(printf("%d agents found\n", rc));
for (iter = list; iter != NULL; iter = iter->next) {
print_result(printf("%s\n", iter->val));
}
lrmd_list_freeall(list);
rc = 0;
} else {
print_result(printf("API_CALL FAILURE - no agents found\n"));
rc = -1;
}
} else if (safe_str_eq(options.api_call, "list_ocf_providers")) {
lrmd_list_t *list = NULL;
lrmd_list_t *iter = NULL;
rc = lrmd_conn->cmds->list_ocf_providers(lrmd_conn, options.type, &list);
if (rc > 0) {
print_result(printf("%d providers found\n", rc));
for (iter = list; iter != NULL; iter = iter->next) {
print_result(printf("%s\n", iter->val));
}
lrmd_list_freeall(list);
rc = 0;
} else {
print_result(printf("API_CALL FAILURE - no providers found\n"));
rc = -1;
}
} else if (safe_str_eq(options.api_call, "list_standards")) {
lrmd_list_t *list = NULL;
lrmd_list_t *iter = NULL;
rc = lrmd_conn->cmds->list_standards(lrmd_conn, &list);
if (rc > 0) {
print_result(printf("%d standards found\n", rc));
for (iter = list; iter != NULL; iter = iter->next) {
print_result(printf("%s\n", iter->val));
}
lrmd_list_freeall(list);
rc = 0;
} else {
print_result(printf("API_CALL FAILURE - no providers found\n"));
rc = -1;
}
} else if (options.api_call) {
print_result(printf("API-CALL FAILURE unknown action '%s'\n", options.action));
test_exit(-1);
}
if (rc < 0) {
print_result(printf("API-CALL FAILURE for '%s' api_rc:%d\n", options.api_call, rc));
test_exit(-1);
}
if (options.api_call && rc == pcmk_ok) {
print_result(printf("API-CALL SUCCESSFUL for '%s'\n", options.api_call));
if (!options.listen) {
test_exit(0);
}
}
if (options.no_wait) {
/* just make the call and exit regardless of anything else. */
test_exit(0);
}
return 0;
}
static resource_t *
find_rsc_or_clone(const char *rsc, pe_working_set_t * data_set)
{
resource_t *the_rsc = pe_find_resource(data_set->resources, rsc);
if (the_rsc == NULL) {
char *as_clone = crm_concat(rsc, "0", ':');
the_rsc = pe_find_resource(data_set->resources, as_clone);
free(as_clone);
}
return the_rsc;
}
static int
generate_params(void)
{
int rc = 0;
pe_working_set_t data_set;
xmlNode *cib_xml_copy = NULL;
resource_t *rsc = NULL;
GHashTable *params = NULL;
GHashTable *meta = NULL;
GHashTableIter iter;
if (options.params) {
return 0;
}
set_working_set_defaults(&data_set);
cib_conn = cib_new();
rc = cib_conn->cmds->signon(cib_conn, "lrmd_test", cib_query);
if (rc != pcmk_ok) {
crm_err("Error signing on to the CIB service: %s\n", pcmk_strerror(rc));
rc = -1;
goto param_gen_bail;
}
cib_xml_copy = get_cib_copy(cib_conn);
if (!cib_xml_copy) {
crm_err("Error retrieving cib copy.");
rc = -1;
goto param_gen_bail;
}
if (cli_config_update(&cib_xml_copy, NULL, FALSE) == FALSE) {
crm_err("Error updating cib configuration");
rc = -1;
goto param_gen_bail;
}
data_set.input = cib_xml_copy;
data_set.now = crm_time_new(NULL);
cluster_status(&data_set);
if (options.rsc_id) {
rsc = find_rsc_or_clone(options.rsc_id, &data_set);
}
if (!rsc) {
crm_err("Resource does not exist in config");
rc = -1;
goto param_gen_bail;
}
params = g_hash_table_new_full(crm_str_hash,
g_str_equal, g_hash_destroy_str, g_hash_destroy_str);
meta = g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str);
get_rsc_attributes(params, rsc, NULL, &data_set);
get_meta_attributes(meta, rsc, NULL, &data_set);
if (params) {
char *key = NULL;
char *value = NULL;
g_hash_table_iter_init(&iter, params);
while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value)) {
options.params = lrmd_key_value_add(options.params, key, value);
}
g_hash_table_destroy(params);
}
if (meta) {
char *key = NULL;
char *value = NULL;
g_hash_table_iter_init(&iter, meta);
while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value)) {
char *crm_name = crm_meta_name(key);
options.params = lrmd_key_value_add(options.params, crm_name, value);
free(crm_name);
}
g_hash_table_destroy(meta);
}
param_gen_bail:
cleanup_alloc_calculations(&data_set);
return rc;
}
int
main(int argc, char **argv)
{
int option_index = 0;
int argerr = 0;
int flag;
char *key = NULL;
char *val = NULL;
gboolean use_tls = FALSE;
crm_trigger_t *trig;
crm_set_options(NULL, "mode [options]", long_options,
"Inject commands into the lrmd and watch for events\n");
while (1) {
flag = crm_get_option(argc, argv, &option_index);
if (flag == -1)
break;
switch (flag) {
case '?':
crm_help(flag, EX_OK);
break;
case 'V':
options.verbose = 1;
break;
case 'Q':
options.quiet = 1;
options.verbose = 0;
break;
case 'l':
options.listen = optarg;
break;
case 'w':
options.no_wait = 1;
break;
case 'R':
options.is_running = 1;
break;
case 'n':
exec_call_opts = lrmd_opt_notify_orig_only;
break;
case 'o':
exec_call_opts = lrmd_opt_notify_changes_only;
break;
case 'c':
options.api_call = optarg;
break;
case 'a':
options.action = optarg;
break;
case 'r':
options.rsc_id = optarg;
break;
case 'x':
options.cancel_call_id = atoi(optarg);
break;
case 'P':
options.provider = optarg;
break;
case 'C':
options.class = optarg;
break;
case 'T':
options.type = optarg;
break;
case 'i':
options.interval = atoi(optarg);
break;
case 't':
options.timeout = atoi(optarg);
break;
case 's':
options.start_delay = atoi(optarg);
break;
case 'k':
key = optarg;
if (key && val) {
options.params = lrmd_key_value_add(options.params, key, val);
key = val = NULL;
}
break;
case 'v':
val = optarg;
if (key && val) {
options.params = lrmd_key_value_add(options.params, key, val);
key = val = NULL;
}
break;
case 'S':
use_tls = TRUE;
break;
default:
++argerr;
break;
}
}
if (argerr) {
crm_help('?', EX_USAGE);
}
if (optind > argc) {
++argerr;
}
if (!options.listen &&
(safe_str_eq(options.api_call, "metadata") ||
safe_str_eq(options.api_call, "list_agents") ||
safe_str_eq(options.api_call, "list_standards") ||
safe_str_eq(options.api_call, "list_ocf_providers"))) {
options.no_connect = 1;
}
crm_log_init("lrmd_ctest", LOG_INFO, TRUE, options.verbose ? TRUE : FALSE, argc, argv, FALSE);
if (options.is_running) {
if (!options.timeout) {
options.timeout = 30000;
}
options.interval = 0;
if (!options.rsc_id) {
crm_err("rsc-id must be given when is-running is used");
test_exit(-1);
}
if (generate_params()) {
print_result(printf
("Failed to retrieve rsc parameters from cib, can not determine if rsc is running.\n"));
test_exit(-1);
}
options.api_call = "exec";
options.action = "monitor";
exec_call_opts = lrmd_opt_notify_orig_only;
}
/* if we can't perform an api_call or listen for events,
* there is nothing to do */
if (!options.api_call && !options.listen) {
crm_err("Nothing to be done. Please specify 'api-call' and/or 'listen'");
return 0;
}
if (use_tls) {
- lrmd_conn = lrmd_remote_api_new("localhost", 0);
+ lrmd_conn = lrmd_remote_api_new(NULL, "localhost", 0);
} else {
lrmd_conn = lrmd_api_new();
}
trig = mainloop_add_trigger(G_PRIORITY_HIGH, start_test, NULL);
mainloop_set_trigger(trig);
mainloop_add_signal(SIGTERM, test_shutdown);
crm_info("Starting");
mainloop = g_main_new(FALSE);
g_main_run(mainloop);
if (cib_conn != NULL) {
cib_conn->cmds->signoff(cib_conn);
cib_delete(cib_conn);
}
test_exit(0);
return 0;
}

File Metadata

Mime Type
text/x-diff
Expires
Mon, Apr 21, 7:11 PM (16 h, 38 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1665430
Default Alt Text
(267 KB)

Event Timeline