Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F7632455
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
82 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/crm/crmd/callbacks.c b/crm/crmd/callbacks.c
index f344b219c5..116b0ed989 100644
--- a/crm/crmd/callbacks.c
+++ b/crm/crmd/callbacks.c
@@ -1,656 +1,664 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <portability.h>
#include <sys/param.h>
#include <crm/crm.h>
#include <string.h>
#include <crmd_fsa.h>
#include <heartbeat.h>
#include <hb_api.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/msg.h>
#include <crm/cib.h>
#include <crmd.h>
#include <crmd_messages.h>
#include <crmd_callbacks.h>
#include <crm/dmalloc_wrapper.h>
GHashTable *crmd_peer_state = NULL;
crm_data_t *find_xml_in_hamessage(const HA_Message * msg);
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);
/* #define MAX_EMPTY_CALLBACKS 20 */
/* int empty_callbacks = 0; */
#define trigger_fsa(source) crm_debug_3("Triggering FSA: %s", __FUNCTION__); \
G_main_set_trigger(source);
gboolean
crmd_ha_msg_dispatch(ll_cluster_t *cluster_conn, gpointer user_data)
{
IPC_Channel *channel = NULL;
gboolean stay_connected = TRUE;
crm_debug_3("Invoked");
if(cluster_conn != NULL) {
channel = cluster_conn->llc_ops->ipcchan(cluster_conn);
}
CRM_CHECK(cluster_conn != NULL, ;);
CRM_CHECK(channel != NULL, ;);
if(channel != NULL && IPC_ISRCONN(channel)) {
if(cluster_conn->llc_ops->msgready(cluster_conn) == 0) {
crm_debug_2("no message ready yet");
}
/* invoke the callbacks but dont block */
cluster_conn->llc_ops->rcvmsg(cluster_conn, 0);
}
if (channel == NULL || channel->ch_status != IPC_CONNECT) {
if(is_set(fsa_input_register, R_HA_DISCONNECTED) == FALSE) {
crm_crit("Lost connection to heartbeat service.");
} else {
crm_info("Lost connection to heartbeat service.");
}
trigger_fsa(fsa_source);
stay_connected = FALSE;
}
return stay_connected;
}
void
crmd_ha_connection_destroy(gpointer user_data)
{
crm_debug_3("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_callback(HA_Message * msg, void* private_data)
{
int level = LOG_DEBUG;
ha_msg_input_t *new_input = NULL;
oc_node_t *from_node = NULL;
const char *from = ha_msg_value(msg, F_ORIG);
const char *seq = ha_msg_value(msg, F_SEQ);
const char *op = ha_msg_value(msg, F_CRM_TASK);
const char *sys_to = ha_msg_value(msg, F_CRM_SYS_TO);
const char *sys_from = ha_msg_value(msg, F_CRM_SYS_FROM);
CRM_DEV_ASSERT(from != NULL);
crm_debug_2("HA[inbound]: %s from %s", op, from);
if(fsa_membership_copy == NULL) {
crm_debug("Ignoring HA messages until we are"
" connected to the CCM (%s op from %s)", op, from);
crm_log_message_adv(
LOG_MSG, "HA[inbound]: Ignore (No CCM)", msg);
return;
}
from_node = g_hash_table_lookup(fsa_membership_copy->members, from);
if(from_node == NULL) {
if(safe_str_eq(op, CRM_OP_VOTE)) {
level = LOG_WARNING;
} else if(AM_I_DC && safe_str_eq(op, CRM_OP_JOIN_ANNOUNCE)) {
level = LOG_WARNING;
} else if(safe_str_eq(sys_from, CRM_SYSTEM_DC)) {
level = LOG_WARNING;
}
crm_log_maybe(level,
"Ignoring HA message (op=%s) from %s: not in our"
" membership list (size=%d)", op, from,
g_hash_table_size(fsa_membership_copy->members));
crm_log_message_adv(LOG_MSG, "HA[inbound]: CCM Discard", msg);
} else if(safe_str_eq(sys_to, CRM_SYSTEM_DC) && AM_I_DC == FALSE) {
crm_debug_2("Ignoring message for the DC [F_SEQ=%s]", seq);
return;
} else if(safe_str_eq(sys_from, CRM_SYSTEM_DC)) {
if(AM_I_DC && safe_str_neq(from, fsa_our_uname)) {
crm_err("Another DC detected: %s (op=%s)", from, op);
/* make sure the election happens NOW */
level = LOG_WARNING;
if(fsa_state != S_ELECTION) {
new_input = new_ha_msg_input(msg);
register_fsa_error_adv(
C_FSA_INTERNAL, I_ELECTION, NULL,
new_input, __FUNCTION__);
}
#if 0
/* still thinking about this one...
* could create a timing issue if we dont notice the
* election before a new DC is elected.
*/
} else if(fsa_our_dc != NULL && safe_str_neq(from,fsa_our_dc)){
crm_warn("Ignoring message from wrong DC: %s vs. %s ",
from, fsa_our_dc);
return;
#endif
} else {
crm_debug_2("Processing DC message from %s [F_SEQ=%s]",
from, seq);
}
}
if(new_input == NULL) {
crm_log_message_adv(LOG_MSG, "HA[inbound]", msg);
new_input = new_ha_msg_input(msg);
route_message(C_HA_MESSAGE, new_input);
}
delete_ha_msg_input(new_input);
trigger_fsa(fsa_source);
return;
}
/*
* Apparently returning TRUE means "stay connected, keep doing stuff".
* Returning FALSE means "we're all done, close the connection"
*/
gboolean
crmd_ipc_msg_callback(IPC_Channel *client, gpointer user_data)
{
int lpc = 0;
HA_Message *msg = NULL;
ha_msg_input_t *new_input = NULL;
crmd_client_t *curr_client = (crmd_client_t*)user_data;
gboolean stay_connected = TRUE;
crm_debug_2("Invoked: %s",
curr_client->table_key);
while(IPC_ISRCONN(client)) {
if(client->ops->is_message_pending(client) == 0) {
break;
}
msg = msgfromIPC_noauth(client);
if (msg == NULL) {
crm_info("%s: no message this time",
curr_client->table_key);
continue;
}
lpc++;
new_input = new_ha_msg_input(msg);
crm_msg_del(msg);
crm_debug_2("Processing msg from %s", curr_client->table_key);
crm_log_message_adv(LOG_DEBUG_2, "CRMd[inbound]", new_input->msg);
if(crmd_authorize_message(new_input, curr_client)) {
route_message(C_IPC_MESSAGE, new_input);
}
delete_ha_msg_input(new_input);
msg = NULL;
new_input = NULL;
if(client->ch_status != IPC_CONNECT) {
break;
}
}
crm_debug_2("Processed %d messages", lpc);
if (client->ch_status != IPC_CONNECT) {
stay_connected = FALSE;
process_client_disconnect(curr_client);
}
trigger_fsa(fsa_source);
return stay_connected;
}
extern GCHSource *lrm_source;
gboolean
lrm_dispatch(IPC_Channel *src_not_used, gpointer user_data)
{
/* ?? src == lrm_channel ?? */
ll_lrm_t *lrm = (ll_lrm_t*)user_data;
IPC_Channel *lrm_channel = lrm->lrm_ops->ipcchan(lrm);
crm_debug_3("Invoked");
lrm->lrm_ops->rcvmsg(lrm, FALSE);
if(lrm_channel->ch_status != IPC_CONNECT) {
if(is_set(fsa_input_register, R_LRM_CONNECTED)) {
crm_crit("LRM Connection failed");
register_fsa_input(C_FSA_INTERNAL, I_ERROR, NULL);
clear_bit_inplace(fsa_input_register, R_LRM_CONNECTED);
} else {
crm_info("LRM Connection disconnected");
}
lrm_source = NULL;
return FALSE;
}
return TRUE;
}
extern gboolean process_lrm_event(lrm_op_t *op);
void
lrm_op_callback(lrm_op_t* op)
{
CRM_CHECK(op != NULL, return);
process_lrm_event(op);
}
void
crmd_ha_status_callback(
const char *node, const char * status, void* private_data)
{
crm_data_t *update = NULL;
crm_notice("Status update: Node %s now has status [%s]",node,status);
if(safe_str_eq(status, DEADSTATUS)) {
/* this node is taost */
update = create_node_state(
node, status, XML_BOOLEAN_NO, OFFLINESTATUS,
CRMD_STATE_INACTIVE, NULL, TRUE, __FUNCTION__);
- crm_xml_add(update, XML_CIB_ATTR_REPLACE, XML_TAG_TRANSIENT_NODEATTRS);
-
+ if(update) {
+ crm_xml_add(update, XML_CIB_ATTR_REPLACE,
+ XML_TAG_TRANSIENT_NODEATTRS);
+ }
+
} else if(safe_str_eq(status, ACTIVESTATUS)) {
update = create_node_state(
- node, status, NULL, NULL, NULL, NULL, FALSE, __FUNCTION__);
+ node, status, NULL, NULL, NULL, NULL,
+ FALSE, __FUNCTION__);
}
if(update != NULL) {
/* this change should not be broadcast */
fsa_cib_anon_update(
XML_CIB_TAG_STATUS, update,
cib_inhibit_bcast|cib_scope_local|cib_quorum_override);
trigger_fsa(fsa_source);
free_xml(update);
+
+ } else {
+ crm_info("Ping node %s is %s", node, status);
}
+
}
void
crmd_client_status_callback(const char * node, const char * client,
- const char * status, void * private)
+ const char * status, void * private)
{
const char *join = NULL;
crm_data_t *update = NULL;
gboolean clear_shutdown = FALSE;
crm_debug_3("Invoked");
if(safe_str_neq(client, CRM_SYSTEM_CRMD)) {
return;
}
if(safe_str_eq(status, JOINSTATUS)){
status = ONLINESTATUS;
clear_shutdown = TRUE;
} else if(safe_str_eq(status, LEAVESTATUS)){
status = OFFLINESTATUS;
join = CRMD_STATE_INACTIVE;
/* clear_shutdown = TRUE; */
}
set_bit_inplace(fsa_input_register, R_PEER_DATA);
g_hash_table_replace(
crmd_peer_state, crm_strdup(node), crm_strdup(status));
if(is_set(fsa_input_register, R_CIB_CONNECTED) == FALSE) {
return;
} else if(fsa_state == S_STOPPING) {
return;
}
crm_notice("Status update: Client %s/%s now has status [%s]",
node, client, status);
if(safe_str_eq(status, ONLINESTATUS)) {
/* remove the cached value in case it changed */
crm_info("Uncaching UUID for %s", node);
unget_uuid(node);
}
if(safe_str_eq(node, fsa_our_dc) && safe_str_eq(status, OFFLINESTATUS)){
/* did our DC leave us */
crm_info("Got client status callback - our DC is dead");
register_fsa_input(C_CRMD_STATUS_CALLBACK, I_ELECTION, NULL);
} else {
crm_debug_3("Got client status callback");
update = create_node_state(
node, NULL, NULL, status, join,
NULL, clear_shutdown, __FUNCTION__);
if(clear_shutdown) {
crm_xml_add(update, XML_CIB_ATTR_REPLACE,
XML_TAG_TRANSIENT_NODEATTRS);
}
/* it is safe to keep these updates on the local node
* each node updates their own CIB
*/
fsa_cib_anon_update(
XML_CIB_TAG_STATUS, update,
cib_inhibit_bcast|cib_scope_local|cib_quorum_override);
free_xml(update);
if(AM_I_DC && safe_str_eq(status, OFFLINESTATUS)) {
g_hash_table_remove(confirmed_nodes, node);
g_hash_table_remove(finalized_nodes, node);
g_hash_table_remove(integrated_nodes, node);
g_hash_table_remove(welcomed_nodes, node);
check_join_state(fsa_state, __FUNCTION__);
}
}
trigger_fsa(fsa_source);
}
static void
crmd_ipc_connection_destroy(gpointer user_data)
{
crmd_client_t *client = user_data;
if(client == NULL) {
crm_debug_4("No client to delete");
return;
}
if(client->client_source != NULL) {
crm_debug_4("Deleting %s (%p) from mainloop",
client->uuid, client->client_source);
G_main_del_IPC_Channel(client->client_source);
client->client_source = NULL;
}
crm_debug_3("Freeing %s client", client->uuid);
crm_free(client->table_key);
crm_free(client->sub_sys);
crm_free(client->uuid);
crm_free(client);
return;
}
gboolean
crmd_client_connect(IPC_Channel *client_channel, gpointer user_data)
{
crm_debug_3("Invoked");
if (client_channel == NULL) {
crm_err("Channel was NULL");
} else if (client_channel->ch_status == IPC_DISCONNECT) {
crm_err("Channel was disconnected");
} else {
crmd_client_t *blank_client = NULL;
crm_debug_3("Channel connected");
crm_malloc0(blank_client, sizeof(crmd_client_t));
if (blank_client == NULL) {
return FALSE;
}
client_channel->ops->set_recv_qlen(client_channel, 100);
client_channel->ops->set_send_qlen(client_channel, 100);
blank_client->client_channel = client_channel;
blank_client->sub_sys = NULL;
blank_client->uuid = NULL;
blank_client->table_key = NULL;
blank_client->client_source =
G_main_add_IPC_Channel(
G_PRIORITY_LOW, client_channel,
FALSE, crmd_ipc_msg_callback,
blank_client, crmd_ipc_connection_destroy);
}
return TRUE;
}
gboolean ccm_dispatch(int fd, gpointer user_data)
{
int rc = 0;
oc_ev_t *ccm_token = (oc_ev_t*)user_data;
gboolean was_error = FALSE;
crm_debug_3("Invoked");
rc = oc_ev_handle_event(ccm_token);
if(rc != 0) {
if(is_set(fsa_input_register, R_CCM_DISCONNECTED) == FALSE) {
/* we signed out, so this is expected */
register_fsa_input(C_CCM_CALLBACK, I_ERROR, NULL);
crm_err("CCM connection appears to have failed: rc=%d.",
rc);
}
was_error = TRUE;
}
trigger_fsa(fsa_source);
return !was_error;
}
static gboolean fsa_have_quorum = FALSE;
void
crmd_ccm_msg_callback(
oc_ed_t event, void *cookie, size_t size, const void *data)
{
int instance = -1;
gboolean update_cache = FALSE;
struct crmd_ccm_data_s *event_data = NULL;
const oc_ev_membership_t *membership = data;
gboolean update_quorum = FALSE;
gboolean trigger_transition = FALSE;
crm_debug_3("Invoked");
if(data != NULL) {
instance = membership->m_instance;
}
crm_info("Quorum %s after event=%s (id=%d)",
ccm_have_quorum(event)?"(re)attained":"lost",
ccm_event_name(event), instance);
switch(event) {
case OC_EV_MS_NEW_MEMBERSHIP:
case OC_EV_MS_INVALID:/* fall through */
update_cache = TRUE;
update_quorum = TRUE;
break;
case OC_EV_MS_NOT_PRIMARY:
#if UNTESTED
if(AM_I_DC == FALSE) {
break;
}
/* tell the TE to pretend it had completed and stop */
/* side effect: we'll end up in S_IDLE */
register_fsa_action(A_TE_HALT, TRUE);
#endif
break;
case OC_EV_MS_PRIMARY_RESTORED:
fsa_membership_copy->id = instance;
if(AM_I_DC && need_transition(fsa_state)) {
trigger_transition = TRUE;
}
break;
case OC_EV_MS_EVICTED:
update_quorum = TRUE;
register_fsa_input(C_FSA_INTERNAL, I_STOP, NULL);
crm_err("Shutting down after CCM event: %s",
ccm_event_name(event));
break;
default:
crm_err("Unknown CCM event: %d", event);
}
if(update_quorum && ccm_have_quorum(event) == FALSE) {
/* did we just loose quorum? */
if(fsa_have_quorum && need_transition(fsa_state)) {
crm_info("Quorum lost: triggering transition (%s)",
ccm_event_name(event));
trigger_transition = TRUE;
}
fsa_have_quorum = FALSE;
} else if(update_quorum) {
crm_debug_2("Updating quorum after event %s",
ccm_event_name(event));
fsa_have_quorum = TRUE;
}
if(trigger_transition) {
crm_debug_2("Scheduling transition after event %s",
ccm_event_name(event));
/* make sure that when we query the CIB that it has
* the changes that triggered the transition
*/
switch(event) {
case OC_EV_MS_NEW_MEMBERSHIP:
case OC_EV_MS_INVALID:
case OC_EV_MS_PRIMARY_RESTORED:
fsa_membership_copy->id = instance;
break;
default:
break;
}
if(update_cache == FALSE) {
/* a stand-alone transition */
register_fsa_action(A_TE_CANCEL);
}
}
if(update_cache) {
crm_debug_2("Updating cache after event %s",
ccm_event_name(event));
crm_malloc0(event_data, sizeof(struct crmd_ccm_data_s));
if(event_data == NULL) { return; }
event_data->event = event;
if(data != NULL) {
event_data->oc = copy_ccm_oc_data(data);
}
register_fsa_input_adv(
C_CCM_CALLBACK, I_CCM_EVENT, event_data,
trigger_transition?A_TE_CANCEL:A_NOTHING,
FALSE, __FUNCTION__);
if (event_data->oc) {
crm_free(event_data->oc);
event_data->oc = NULL;
}
crm_free(event_data);
}
oc_ev_callback_done(cookie);
return;
}
void
crmd_cib_connection_destroy(gpointer user_data)
{
crm_debug_3("Invoked");
trigger_fsa(fsa_source);
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_inplace(fsa_input_register, R_CIB_CONNECTED);
return;
}
longclock_t fsa_start = 0;
longclock_t fsa_stop = 0;
longclock_t fsa_diff = 0;
gboolean
crm_fsa_trigger(gpointer user_data)
{
unsigned int fsa_diff_ms = 0;
if(fsa_diff_max_ms > 0) {
fsa_start = time_longclock();
}
crm_debug_2("Invoked (queue len: %d)", g_list_length(fsa_message_queue));
s_crmd_fsa(C_FSA_INTERNAL);
crm_debug_2("Exited (queue len: %d)", g_list_length(fsa_message_queue));
if(fsa_diff_max_ms > 0) {
fsa_stop = time_longclock();
fsa_diff = sub_longclock(fsa_stop, fsa_start);
fsa_diff_ms = longclockto_ms(fsa_diff);
if(fsa_diff_ms > fsa_diff_max_ms) {
crm_err("FSA took %dms to complete", fsa_diff_ms);
} else if(fsa_diff_ms > fsa_diff_warn_ms) {
crm_warn("FSA took %dms to complete", fsa_diff_ms);
}
}
return TRUE;
}
diff --git a/crm/crmd/utils.c b/crm/crmd/utils.c
index 13d6290261..46c3c3496b 100644
--- a/crm/crmd/utils.c
+++ b/crm/crmd/utils.c
@@ -1,1389 +1,1395 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <portability.h>
#include <sys/param.h>
#include <crm/crm.h>
#include <crm/cib.h>
#include <crmd_fsa.h>
#include <clplumbing/Gmain_timeout.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <heartbeat.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/msg.h>
#include <crmd_messages.h>
#include <crmd_utils.h>
#include <crm/dmalloc_wrapper.h>
void copy_ccm_node(oc_node_t a_node, oc_node_t *a_node_copy);
/* A_DC_TIMER_STOP, A_DC_TIMER_START,
* A_FINALIZE_TIMER_STOP, A_FINALIZE_TIMER_START
* A_INTEGRATE_TIMER_STOP, A_INTEGRATE_TIMER_START
*/
enum crmd_fsa_input
do_timer_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 timer_op_ok = TRUE;
enum crmd_fsa_input result = I_NULL;
if(action & A_DC_TIMER_STOP) {
timer_op_ok = crm_timer_stop(election_trigger);
} else if(action & A_FINALIZE_TIMER_STOP) {
timer_op_ok = crm_timer_stop(finalization_timer);
} else if(action & A_INTEGRATE_TIMER_STOP) {
timer_op_ok = crm_timer_stop(integration_timer);
/* } else if(action & A_ELECTION_TIMEOUT_STOP) { */
/* timer_op_ok = crm_timer_stop(election_timeout); */
}
/* dont start a timer that wasnt already running */
if(action & A_DC_TIMER_START && timer_op_ok) {
crm_timer_start(election_trigger);
if(AM_I_DC) {
/* there can be only one */
result = I_ELECTION;
}
} else if(action & A_FINALIZE_TIMER_START) {
crm_timer_start(finalization_timer);
} else if(action & A_INTEGRATE_TIMER_START) {
crm_timer_start(integration_timer);
/* } else if(action & A_ELECTION_TIMEOUT_START) { */
/* crm_timer_start(election_timeout); */
}
return I_NULL;
}
static const char *
get_timer_desc(fsa_timer_t *timer)
{
if(timer == election_trigger) {
return "Election Trigger";
} else if(timer == election_timeout) {
return "Election Timeout";
} else if(timer == shutdown_escalation_timer) {
return "Shutdown Escalation";
} else if(timer == integration_timer) {
return "Integration Timer";
} else if(timer == finalization_timer) {
return "Finalization Timer";
} else if(timer == wait_timer) {
return "Wait Timer";
} else if(timer == recheck_timer) {
return "PEngine Recheck Timer";
}
return "Unknown Timer";
}
gboolean
crm_timer_popped(gpointer data)
{
fsa_timer_t *timer = (fsa_timer_t *)data;
if(timer == wait_timer
|| timer == recheck_timer
|| timer == finalization_timer
|| timer == election_trigger) {
crm_info("%s (%s) just popped!",
get_timer_desc(timer),
fsa_input2string(timer->fsa_input));
} else {
crm_err("%s (%s) just popped!",
get_timer_desc(timer),
fsa_input2string(timer->fsa_input));
}
if(timer->repeat == FALSE) {
crm_timer_stop(timer); /* make it _not_ go off again */
}
if(timer->fsa_input == I_INTEGRATED) {
register_fsa_input_before(
C_TIMER_POPPED, timer->fsa_input, NULL);
} else if(timer->fsa_input == I_PE_CALC
&& fsa_state != S_IDLE) {
crm_debug("Discarding %s event in state: %s",
fsa_input2string(timer->fsa_input),
fsa_state2string(fsa_state));
} else if(timer->fsa_input == I_FINALIZED
&& fsa_state != S_FINALIZE_JOIN) {
crm_debug("Discarding %s event in state: %s",
fsa_input2string(timer->fsa_input),
fsa_state2string(fsa_state));
} else if(timer->fsa_input != I_NULL) {
register_fsa_input(C_TIMER_POPPED, timer->fsa_input, NULL);
}
crm_debug_3("Triggering FSA: %s", __FUNCTION__);
G_main_set_trigger(fsa_source);
return TRUE;
}
gboolean
crm_timer_start(fsa_timer_t *timer)
{
const char *timer_desc = get_timer_desc(timer);
if(timer->source_id == 0 && timer->period_ms > 0) {
timer->source_id = Gmain_timeout_add(
timer->period_ms, timer->callback, (void*)timer);
CRM_ASSERT(timer->source_id != 0);
crm_debug("Started %s (%s:%dms), src=%d",
timer_desc, fsa_input2string(timer->fsa_input),
timer->period_ms, timer->source_id);
} else if(timer->period_ms < 0) {
crm_err("Tried to start %s (%s:%dms) with a -ve period",
timer_desc, fsa_input2string(timer->fsa_input),
timer->period_ms);
} else {
crm_debug("%s (%s:%dms) already running: src=%d",
timer_desc, fsa_input2string(timer->fsa_input),
timer->period_ms, timer->source_id);
return FALSE;
}
return TRUE;
}
gboolean
crm_timer_stop(fsa_timer_t *timer)
{
const char *timer_desc = get_timer_desc(timer);
if(timer == NULL) {
crm_err("Attempted to stop NULL timer");
return FALSE;
} else if(timer->source_id != 0) {
crm_debug("Stopping %s (%s:%dms), src=%d",
timer_desc, fsa_input2string(timer->fsa_input),
timer->period_ms, timer->source_id);
Gmain_timeout_remove(timer->source_id);
timer->source_id = 0;
} else {
crm_debug_2("%s (%s:%dms) already stopped",
timer_desc, fsa_input2string(timer->fsa_input),
timer->period_ms);
return FALSE;
}
return TRUE;
}
long long
toggle_bit(long long action_list, long long action)
{
crm_debug_5("Toggling bit %.16llx", action);
action_list ^= action;
crm_debug_5("Result %.16llx", action_list & action);
return action_list;
}
long long
clear_bit(long long action_list, long long action)
{
unsigned int level = LOG_DEBUG_5;
crm_log_maybe(level, "Clearing bit\t%.16llx", action);
/* ensure its set */
action_list |= action;
/* then toggle */
action_list = action_list ^ action;
return action_list;
}
long long
set_bit(long long action_list, long long action)
{
unsigned int level = LOG_DEBUG_5;
crm_log_maybe(level, "Setting bit\t%.16llx", action);
action_list |= action;
return action_list;
}
gboolean
is_set(long long action_list, long long action)
{
crm_debug_5("Checking bit\t%.16llx in %.16llx", action, action_list);
return ((action_list & action) == action);
}
gboolean
is_set_any(long long action_list, long long action)
{
crm_debug_5("Checking bit\t%.16llx in %.16llx", action, action_list);
return ((action_list & action) != 0);
}
const char *
fsa_input2string(enum crmd_fsa_input input)
{
const char *inputAsText = NULL;
switch(input){
case I_NULL:
inputAsText = "I_NULL";
break;
case I_CCM_EVENT:
inputAsText = "I_CCM_EVENT";
break;
case I_CIB_OP:
inputAsText = "I_CIB_OP";
break;
case I_CIB_UPDATE:
inputAsText = "I_CIB_UPDATE";
break;
case I_DC_TIMEOUT:
inputAsText = "I_DC_TIMEOUT";
break;
case I_ELECTION:
inputAsText = "I_ELECTION";
break;
case I_PE_CALC:
inputAsText = "I_PE_CALC";
break;
case I_RELEASE_DC:
inputAsText = "I_RELEASE_DC";
break;
case I_ELECTION_DC:
inputAsText = "I_ELECTION_DC";
break;
case I_ERROR:
inputAsText = "I_ERROR";
break;
case I_FAIL:
inputAsText = "I_FAIL";
break;
case I_INTEGRATED:
inputAsText = "I_INTEGRATED";
break;
case I_FINALIZED:
inputAsText = "I_FINALIZED";
break;
case I_NODE_JOIN:
inputAsText = "I_NODE_JOIN";
break;
case I_JOIN_OFFER:
inputAsText = "I_JOIN_OFFER";
break;
case I_JOIN_REQUEST:
inputAsText = "I_JOIN_REQUEST";
break;
case I_JOIN_RESULT:
inputAsText = "I_JOIN_RESULT";
break;
case I_NOT_DC:
inputAsText = "I_NOT_DC";
break;
case I_RECOVERED:
inputAsText = "I_RECOVERED";
break;
case I_RELEASE_FAIL:
inputAsText = "I_RELEASE_FAIL";
break;
case I_RELEASE_SUCCESS:
inputAsText = "I_RELEASE_SUCCESS";
break;
case I_RESTART:
inputAsText = "I_RESTART";
break;
case I_PE_SUCCESS:
inputAsText = "I_PE_SUCCESS";
break;
case I_ROUTER:
inputAsText = "I_ROUTER";
break;
case I_SHUTDOWN:
inputAsText = "I_SHUTDOWN";
break;
case I_STARTUP:
inputAsText = "I_STARTUP";
break;
case I_TE_SUCCESS:
inputAsText = "I_TE_SUCCESS";
break;
case I_STOP:
inputAsText = "I_STOP";
break;
case I_DC_HEARTBEAT:
inputAsText = "I_DC_HEARTBEAT";
break;
case I_WAIT_FOR_EVENT:
inputAsText = "I_WAIT_FOR_EVENT";
break;
case I_LRM_EVENT:
inputAsText = "I_LRM_EVENT";
break;
case I_PENDING:
inputAsText = "I_PENDING";
break;
case I_HALT:
inputAsText = "I_HALT";
break;
case I_TERMINATE:
inputAsText = "I_TERMINATE";
break;
case I_ILLEGAL:
inputAsText = "I_ILLEGAL";
break;
}
if(inputAsText == NULL) {
crm_err("Input %d is unknown", input);
inputAsText = "<UNKNOWN_INPUT>";
}
return inputAsText;
}
const char *
fsa_state2string(enum crmd_fsa_state state)
{
const char *stateAsText = NULL;
switch(state){
case S_IDLE:
stateAsText = "S_IDLE";
break;
case S_ELECTION:
stateAsText = "S_ELECTION";
break;
case S_INTEGRATION:
stateAsText = "S_INTEGRATION";
break;
case S_FINALIZE_JOIN:
stateAsText = "S_FINALIZE_JOIN";
break;
case S_NOT_DC:
stateAsText = "S_NOT_DC";
break;
case S_POLICY_ENGINE:
stateAsText = "S_POLICY_ENGINE";
break;
case S_RECOVERY:
stateAsText = "S_RECOVERY";
break;
case S_RELEASE_DC:
stateAsText = "S_RELEASE_DC";
break;
case S_PENDING:
stateAsText = "S_PENDING";
break;
case S_STOPPING:
stateAsText = "S_STOPPING";
break;
case S_TERMINATE:
stateAsText = "S_TERMINATE";
break;
case S_TRANSITION_ENGINE:
stateAsText = "S_TRANSITION_ENGINE";
break;
case S_STARTING:
stateAsText = "S_STARTING";
break;
case S_HALT:
stateAsText = "S_HALT";
break;
case S_ILLEGAL:
stateAsText = "S_ILLEGAL";
break;
}
if(stateAsText == NULL) {
crm_err("State %d is unknown", state);
stateAsText = "<UNKNOWN_STATE>";
}
return stateAsText;
}
const char *
fsa_cause2string(enum crmd_fsa_cause cause)
{
const char *causeAsText = NULL;
switch(cause){
case C_UNKNOWN:
causeAsText = "C_UNKNOWN";
break;
case C_STARTUP:
causeAsText = "C_STARTUP";
break;
case C_IPC_MESSAGE:
causeAsText = "C_IPC_MESSAGE";
break;
case C_HA_MESSAGE:
causeAsText = "C_HA_MESSAGE";
break;
case C_CCM_CALLBACK:
causeAsText = "C_CCM_CALLBACK";
break;
case C_TIMER_POPPED:
causeAsText = "C_TIMER_POPPED";
break;
case C_SHUTDOWN:
causeAsText = "C_SHUTDOWN";
break;
case C_HEARTBEAT_FAILED:
causeAsText = "C_HEARTBEAT_FAILED";
break;
case C_SUBSYSTEM_CONNECT:
causeAsText = "C_SUBSYSTEM_CONNECT";
break;
case C_LRM_OP_CALLBACK:
causeAsText = "C_LRM_OP_CALLBACK";
break;
case C_LRM_MONITOR_CALLBACK:
causeAsText = "C_LRM_MONITOR_CALLBACK";
break;
case C_CRMD_STATUS_CALLBACK:
causeAsText = "C_CRMD_STATUS_CALLBACK";
break;
case C_HA_DISCONNECT:
causeAsText = "C_HA_DISCONNECT";
break;
case C_FSA_INTERNAL:
causeAsText = "C_FSA_INTERNAL";
break;
case C_ILLEGAL:
causeAsText = "C_ILLEGAL";
break;
}
if(causeAsText == NULL) {
crm_err("Cause %d is unknown", cause);
causeAsText = "<UNKNOWN_CAUSE>";
}
return causeAsText;
}
const char *
fsa_action2string(long long action)
{
const char *actionAsText = NULL;
switch(action){
case A_NOTHING:
actionAsText = "A_NOTHING";
break;
case A_ELECTION_START:
actionAsText = "A_ELECTION_START";
break;
case A_READCONFIG:
actionAsText = "A_READCONFIG";
break;
case O_RELEASE:
actionAsText = "O_RELEASE";
break;
case A_STARTUP:
actionAsText = "A_STARTUP";
break;
case A_STARTED:
actionAsText = "A_STARTED";
break;
case A_HA_CONNECT:
actionAsText = "A_HA_CONNECT";
break;
case A_HA_DISCONNECT:
actionAsText = "A_HA_DISCONNECT";
break;
case A_LRM_CONNECT:
actionAsText = "A_LRM_CONNECT";
break;
case A_LRM_EVENT:
actionAsText = "A_LRM_EVENT";
break;
case A_LRM_INVOKE:
actionAsText = "A_LRM_INVOKE";
break;
case A_LRM_DISCONNECT:
actionAsText = "A_LRM_DISCONNECT";
break;
case A_CL_JOIN_QUERY:
actionAsText = "A_CL_JOIN_QUERY";
break;
case A_DC_TIMER_STOP:
actionAsText = "A_DC_TIMER_STOP";
break;
case A_DC_TIMER_START:
actionAsText = "A_DC_TIMER_START";
break;
case A_INTEGRATE_TIMER_START:
actionAsText = "A_INTEGRATE_TIMER_START";
break;
case A_INTEGRATE_TIMER_STOP:
actionAsText = "A_INTEGRATE_TIMER_STOP";
break;
case A_FINALIZE_TIMER_START:
actionAsText = "A_FINALIZE_TIMER_START";
break;
case A_FINALIZE_TIMER_STOP:
actionAsText = "A_FINALIZE_TIMER_STOP";
break;
case A_ELECTION_COUNT:
actionAsText = "A_ELECTION_COUNT";
break;
case A_ELECTION_VOTE:
actionAsText = "A_ELECTION_VOTE";
break;
case A_ELECTION_CHECK:
actionAsText = "A_ELECTION_CHECK";
break;
case A_CL_JOIN_ANNOUNCE:
actionAsText = "A_CL_JOIN_ANNOUNCE";
break;
case A_CL_JOIN_REQUEST:
actionAsText = "A_CL_JOIN_REQUEST";
break;
case A_CL_JOIN_RESULT:
actionAsText = "A_CL_JOIN_RESULT";
break;
case A_DC_JOIN_OFFER_ALL:
actionAsText = "A_DC_JOIN_OFFER_ALL";
break;
case A_DC_JOIN_OFFER_ONE:
actionAsText = "A_DC_JOIN_OFFER_ONE";
break;
case A_DC_JOIN_PROCESS_REQ:
actionAsText = "A_DC_JOIN_PROCESS_REQ";
break;
case A_DC_JOIN_PROCESS_ACK:
actionAsText = "A_DC_JOIN_PROCESS_ACK";
break;
case A_DC_JOIN_FINALIZE:
actionAsText = "A_DC_JOIN_FINALIZE";
break;
case A_MSG_PROCESS:
actionAsText = "A_MSG_PROCESS";
break;
case A_MSG_ROUTE:
actionAsText = "A_MSG_ROUTE";
break;
case A_RECOVER:
actionAsText = "A_RECOVER";
break;
case A_DC_RELEASE:
actionAsText = "A_DC_RELEASE";
break;
case A_DC_RELEASED:
actionAsText = "A_DC_RELEASED";
break;
case A_DC_TAKEOVER:
actionAsText = "A_DC_TAKEOVER";
break;
case A_SHUTDOWN:
actionAsText = "A_SHUTDOWN";
break;
case A_SHUTDOWN_REQ:
actionAsText = "A_SHUTDOWN_REQ";
break;
case A_STOP:
actionAsText = "A_STOP ";
break;
case A_EXIT_0:
actionAsText = "A_EXIT_0";
break;
case A_EXIT_1:
actionAsText = "A_EXIT_1";
break;
case A_CCM_CONNECT:
actionAsText = "A_CCM_CONNECT";
break;
case A_CCM_DISCONNECT:
actionAsText = "A_CCM_DISCONNECT";
break;
case A_CCM_EVENT:
actionAsText = "A_CCM_EVENT";
break;
case A_CCM_UPDATE_CACHE:
actionAsText = "A_CCM_UPDATE_CACHE";
break;
case A_CIB_BUMPGEN:
actionAsText = "A_CIB_BUMPGEN";
break;
case A_CIB_INVOKE:
actionAsText = "A_CIB_INVOKE";
break;
case O_CIB_RESTART:
actionAsText = "O_CIB_RESTART";
break;
case A_CIB_START:
actionAsText = "A_CIB_START";
break;
case A_CIB_STOP:
actionAsText = "A_CIB_STOP";
break;
case A_TE_INVOKE:
actionAsText = "A_TE_INVOKE";
break;
case O_TE_RESTART:
actionAsText = "O_TE_RESTART";
break;
case A_TE_START:
actionAsText = "A_TE_START";
break;
case A_TE_STOP:
actionAsText = "A_TE_STOP";
break;
case A_TE_HALT:
actionAsText = "A_TE_HALT";
break;
case A_TE_CANCEL:
actionAsText = "A_TE_CANCEL";
break;
case A_PE_INVOKE:
actionAsText = "A_PE_INVOKE";
break;
case O_PE_RESTART:
actionAsText = "O_PE_RESTART";
break;
case A_PE_START:
actionAsText = "A_PE_START";
break;
case A_PE_STOP:
actionAsText = "A_PE_STOP";
break;
case A_NODE_BLOCK:
actionAsText = "A_NODE_BLOCK";
break;
case A_UPDATE_NODESTATUS:
actionAsText = "A_UPDATE_NODESTATUS";
break;
case A_LOG:
actionAsText = "A_LOG ";
break;
case A_ERROR:
actionAsText = "A_ERROR ";
break;
case A_WARN:
actionAsText = "A_WARN ";
break;
}
if(actionAsText == NULL) {
crm_err("Action %.16llx is unknown", action);
actionAsText = "<UNKNOWN_ACTION>";
}
return actionAsText;
}
void
fsa_dump_inputs(int log_level, const char *text, long long input_register)
{
if(input_register == A_NOTHING) {
return;
}
if(text == NULL) {
text = "Input register contents:";
}
if(is_set(input_register, R_THE_DC)) {
crm_log_maybe(log_level,
"%s %.16llx (R_THE_DC)", text, R_THE_DC);
}
if(is_set(input_register, R_STARTING)) {
crm_log_maybe(log_level, "%s %.16llx (R_STARTING)",
text, R_STARTING);
}
if(is_set(input_register, R_SHUTDOWN)) {
crm_log_maybe(log_level, "%s %.16llx (R_SHUTDOWN)",
text, R_SHUTDOWN);
}
if(is_set(input_register, R_STAYDOWN)) {
crm_log_maybe(log_level, "%s %.16llx (R_STAYDOWN)",
text, R_STAYDOWN);
}
if(is_set(input_register, R_JOIN_OK)) {
crm_log_maybe(log_level, "%s %.16llx (R_JOIN_OK)",
text, R_JOIN_OK);
}
if(is_set(input_register, R_READ_CONFIG)) {
crm_log_maybe(log_level, "%s %.16llx (R_READ_CONFIG)",
text, R_READ_CONFIG);
}
if(is_set(input_register, R_INVOKE_PE)) {
crm_log_maybe(log_level, "%s %.16llx (R_INVOKE_PE)",
text, R_INVOKE_PE);
}
if(is_set(input_register, R_CIB_CONNECTED)) {
crm_log_maybe(log_level, "%s %.16llx (R_CIB_CONNECTED)",
text, R_CIB_CONNECTED);
}
if(is_set(input_register, R_PE_CONNECTED)) {
crm_log_maybe(log_level, "%s %.16llx (R_PE_CONNECTED)",
text, R_PE_CONNECTED);
}
if(is_set(input_register, R_TE_CONNECTED)) {
crm_log_maybe(log_level, "%s %.16llx (R_TE_CONNECTED)",
text, R_TE_CONNECTED);
}
if(is_set(input_register, R_LRM_CONNECTED)) {
crm_log_maybe(log_level, "%s %.16llx (R_LRM_CONNECTED)",
text, R_LRM_CONNECTED);
}
if(is_set(input_register, R_CIB_REQUIRED)) {
crm_log_maybe(log_level, "%s %.16llx (R_CIB_REQUIRED)",
text, R_CIB_REQUIRED);
}
if(is_set(input_register, R_PE_REQUIRED)) {
crm_log_maybe(log_level, "%s %.16llx (R_PE_REQUIRED)",
text, R_PE_REQUIRED);
}
if(is_set(input_register, R_TE_REQUIRED)) {
crm_log_maybe(log_level, "%s %.16llx (R_TE_REQUIRED)",
text, R_TE_REQUIRED);
}
if(is_set(input_register, R_REQ_PEND)) {
crm_log_maybe(log_level, "%s %.16llx (R_REQ_PEND)",
text, R_REQ_PEND);
}
if(is_set(input_register, R_PE_PEND)) {
crm_log_maybe(log_level, "%s %.16llx (R_PE_PEND)",
text, R_PE_PEND);
}
if(is_set(input_register, R_TE_PEND)) {
crm_log_maybe(log_level, "%s %.16llx (R_TE_PEND)",
text, R_TE_PEND);
}
if(is_set(input_register, R_RESP_PEND)) {
crm_log_maybe(log_level, "%s %.16llx (R_RESP_PEND)",
text, R_RESP_PEND);
}
if(is_set(input_register, R_CIB_DONE)) {
crm_log_maybe(log_level, "%s %.16llx (R_CIB_DONE)",
text, R_CIB_DONE);
}
if(is_set(input_register, R_HAVE_CIB)) {
crm_log_maybe(log_level, "%s %.16llx (R_HAVE_CIB)",
text, R_HAVE_CIB);
}
if(is_set(input_register, R_CIB_ASKED)) {
crm_log_maybe(log_level, "%s %.16llx (R_CIB_ASKED)",
text, R_CIB_ASKED);
}
if(is_set(input_register, R_CCM_DATA)) {
crm_log_maybe(log_level, "%s %.16llx (R_CCM_DATA)",
text, R_CCM_DATA);
}
if(is_set(input_register, R_PEER_DATA)) {
crm_log_maybe(log_level, "%s %.16llx (R_PEER_DATA)",
text, R_PEER_DATA);
}
if(is_set(input_register, R_IN_RECOVERY)) {
crm_log_maybe(log_level, "%s %.16llx (R_IN_RECOVERY)",
text, R_IN_RECOVERY);
}
}
void
fsa_dump_actions(long long action, const char *text)
{
int log_level = LOG_DEBUG_3;
if(is_set(action, A_READCONFIG)) {
crm_log_maybe(log_level,
"Action %.16llx (A_READCONFIG) %s", A_READCONFIG, text);
}
if(is_set(action, A_STARTUP)) {
crm_log_maybe(log_level,
"Action %.16llx (A_STARTUP) %s", A_STARTUP, text);
}
if(is_set(action, A_STARTED)) {
crm_log_maybe(log_level,
"Action %.16llx (A_STARTED) %s", A_STARTED, text);
}
if(is_set(action, A_HA_CONNECT)) {
crm_log_maybe(log_level,
"Action %.16llx (A_CONNECT) %s", A_HA_CONNECT, text);
}
if(is_set(action, A_HA_DISCONNECT)) {
crm_log_maybe(log_level,
"Action %.16llx (A_DISCONNECT) %s",
A_HA_DISCONNECT, text);
}
if(is_set(action, A_LRM_CONNECT)) {
crm_log_maybe(log_level,
"Action %.16llx (A_LRM_CONNECT) %s",
A_LRM_CONNECT, text);
}
if(is_set(action, A_LRM_EVENT)) {
crm_log_maybe(log_level,
"Action %.16llx (A_LRM_EVENT) %s",
A_LRM_EVENT, text);
}
if(is_set(action, A_LRM_INVOKE)) {
crm_log_maybe(log_level,
"Action %.16llx (A_LRM_INVOKE) %s",
A_LRM_INVOKE, text);
}
if(is_set(action, A_LRM_DISCONNECT)) {
crm_log_maybe(log_level,
"Action %.16llx (A_LRM_DISCONNECT) %s",
A_LRM_DISCONNECT, text);
}
if(is_set(action, A_DC_TIMER_STOP)) {
crm_log_maybe(log_level,
"Action %.16llx (A_DC_TIMER_STOP) %s",
A_DC_TIMER_STOP, text);
}
if(is_set(action, A_DC_TIMER_START)) {
crm_log_maybe(log_level,
"Action %.16llx (A_DC_TIMER_START) %s",
A_DC_TIMER_START, text);
}
if(is_set(action, A_INTEGRATE_TIMER_START)) {
crm_log_maybe(log_level,
"Action %.16llx (A_INTEGRATE_TIMER_START) %s",
A_INTEGRATE_TIMER_START, text);
}
if(is_set(action, A_INTEGRATE_TIMER_STOP)) {
crm_log_maybe(log_level,
"Action %.16llx (A_INTEGRATE_TIMER_STOP) %s",
A_INTEGRATE_TIMER_STOP, text);
}
if(is_set(action, A_FINALIZE_TIMER_START)) {
crm_log_maybe(log_level,
"Action %.16llx (A_FINALIZE_TIMER_START) %s",
A_FINALIZE_TIMER_START, text);
}
if(is_set(action, A_FINALIZE_TIMER_STOP)) {
crm_log_maybe(log_level,
"Action %.16llx (A_FINALIZE_TIMER_STOP) %s",
A_FINALIZE_TIMER_STOP, text);
}
if(is_set(action, A_ELECTION_COUNT)) {
crm_log_maybe(log_level,
"Action %.16llx (A_ELECTION_COUNT) %s",
A_ELECTION_COUNT, text);
}
if(is_set(action, A_ELECTION_VOTE)) {
crm_log_maybe(log_level,
"Action %.16llx (A_ELECTION_VOTE) %s",
A_ELECTION_VOTE, text);
}
if(is_set(action, A_ELECTION_CHECK)) {
crm_log_maybe(log_level,
"Action %.16llx (A_ELECTION_CHECK) %s",
A_ELECTION_CHECK, text);
}
if(is_set(action, A_CL_JOIN_ANNOUNCE)) {
crm_log_maybe(log_level,
"Action %.16llx (A_CL_JOIN_ANNOUNCE) %s",
A_CL_JOIN_ANNOUNCE, text);
}
if(is_set(action, A_CL_JOIN_REQUEST)) {
crm_log_maybe(log_level,
"Action %.16llx (A_CL_JOIN_REQUEST) %s",
A_CL_JOIN_REQUEST, text);
}
if(is_set(action, A_CL_JOIN_RESULT)) {
crm_log_maybe(log_level,
"Action %.16llx (A_CL_JOIN_RESULT) %s",
A_CL_JOIN_RESULT, text);
}
if(is_set(action, A_DC_JOIN_OFFER_ALL)) {
crm_log_maybe(log_level,
"Action %.16llx (A_DC_JOIN_OFFER_ALL) %s",
A_DC_JOIN_OFFER_ALL, text);
}
if(is_set(action, A_DC_JOIN_OFFER_ONE)) {
crm_log_maybe(log_level,
"Action %.16llx (A_DC_JOIN_OFFER_ONE) %s",
A_DC_JOIN_OFFER_ONE, text);
}
if(is_set(action, A_DC_JOIN_PROCESS_REQ)) {
crm_log_maybe(log_level,
"Action %.16llx (A_DC_JOIN_PROCESS_REQ) %s",
A_DC_JOIN_PROCESS_REQ, text);
}
if(is_set(action, A_DC_JOIN_PROCESS_ACK)) {
crm_log_maybe(log_level,
"Action %.16llx (A_DC_JOIN_PROCESS_ACK) %s",
A_DC_JOIN_PROCESS_ACK, text);
}
if(is_set(action, A_DC_JOIN_FINALIZE)) {
crm_log_maybe(log_level,
"Action %.16llx (A_DC_JOIN_FINALIZE) %s",
A_DC_JOIN_FINALIZE, text);
}
if(is_set(action, A_MSG_PROCESS)) {
crm_log_maybe(log_level,
"Action %.16llx (A_MSG_PROCESS) %s",
A_MSG_PROCESS, text);
}
if(is_set(action, A_MSG_ROUTE)) {
crm_log_maybe(log_level,
"Action %.16llx (A_MSG_ROUTE) %s",
A_MSG_ROUTE, text);
}
if(is_set(action, A_RECOVER)) {
crm_log_maybe(log_level,
"Action %.16llx (A_RECOVER) %s",
A_RECOVER, text);
}
if(is_set(action, A_DC_RELEASE)) {
crm_log_maybe(log_level,
"Action %.16llx (A_DC_RELEASE) %s",
A_DC_RELEASE, text);
}
if(is_set(action, A_DC_RELEASED)) {
crm_log_maybe(log_level,
"Action %.16llx (A_DC_RELEASED) %s",
A_DC_RELEASED, text);
}
if(is_set(action, A_DC_TAKEOVER)) {
crm_log_maybe(log_level,
"Action %.16llx (A_DC_TAKEOVER) %s",
A_DC_TAKEOVER, text);
}
if(is_set(action, A_SHUTDOWN)) {
crm_log_maybe(log_level,
"Action %.16llx (A_SHUTDOWN) %s", A_SHUTDOWN, text);
}
if(is_set(action, A_SHUTDOWN_REQ)) {
crm_log_maybe(log_level,
"Action %.16llx (A_SHUTDOWN_REQ) %s",
A_SHUTDOWN_REQ, text);
}
if(is_set(action, A_STOP)) {
crm_log_maybe(log_level,
"Action %.16llx (A_STOP ) %s", A_STOP , text);
}
if(is_set(action, A_EXIT_0)) {
crm_log_maybe(log_level,
"Action %.16llx (A_EXIT_0) %s", A_EXIT_0, text);
}
if(is_set(action, A_EXIT_1)) {
crm_log_maybe(log_level,
"Action %.16llx (A_EXIT_1) %s", A_EXIT_1, text);
}
if(is_set(action, A_CCM_CONNECT)) {
crm_log_maybe(log_level,
"Action %.16llx (A_CCM_CONNECT) %s",
A_CCM_CONNECT, text);
}
if(is_set(action, A_CCM_DISCONNECT)) {
crm_log_maybe(log_level,
"Action %.16llx (A_CCM_DISCONNECT) %s",
A_CCM_DISCONNECT, text);
}
if(is_set(action, A_CCM_EVENT)) {
crm_log_maybe(log_level,
"Action %.16llx (A_CCM_EVENT) %s",
A_CCM_EVENT, text);
}
if(is_set(action, A_CCM_UPDATE_CACHE)) {
crm_log_maybe(log_level,
"Action %.16llx (A_CCM_UPDATE_CACHE) %s",
A_CCM_UPDATE_CACHE, text);
}
if(is_set(action, A_CIB_BUMPGEN)) {
crm_log_maybe(log_level,
"Action %.16llx (A_CIB_BUMPGEN) %s",
A_CIB_BUMPGEN, text);
}
if(is_set(action, A_CIB_INVOKE)) {
crm_log_maybe(log_level,
"Action %.16llx (A_CIB_INVOKE) %s",
A_CIB_INVOKE, text);
}
if(is_set(action, A_CIB_START)) {
crm_log_maybe(log_level,
"Action %.16llx (A_CIB_START) %s",
A_CIB_START, text);
}
if(is_set(action, A_CIB_STOP)) {
crm_log_maybe(log_level,
"Action %.16llx (A_CIB_STOP) %s", A_CIB_STOP, text);
}
if(is_set(action, A_TE_INVOKE)) {
crm_log_maybe(log_level,
"Action %.16llx (A_TE_INVOKE) %s", A_TE_INVOKE, text);
}
if(is_set(action, A_TE_START)) {
crm_log_maybe(log_level,
"Action %.16llx (A_TE_START) %s",
A_TE_START, text);
}
if(is_set(action, A_TE_STOP)) {
crm_log_maybe(log_level,
"Action %.16llx (A_TE_STOP) %s", A_TE_STOP, text);
}
if(is_set(action, A_TE_CANCEL)) {
crm_log_maybe(log_level,
"Action %.16llx (A_TE_CANCEL) %s",
A_TE_CANCEL, text);
}
if(is_set(action, A_PE_INVOKE)) {
crm_log_maybe(log_level,
"Action %.16llx (A_PE_INVOKE) %s",
A_PE_INVOKE, text);
}
if(is_set(action, A_PE_START)) {
crm_log_maybe(log_level,
"Action %.16llx (A_PE_START) %s", A_PE_START, text);
}
if(is_set(action, A_PE_STOP)) {
crm_log_maybe(log_level,
"Action %.16llx (A_PE_STOP) %s", A_PE_STOP, text);
}
if(is_set(action, A_NODE_BLOCK)) {
crm_log_maybe(log_level,
"Action %.16llx (A_NODE_BLOCK) %s",
A_NODE_BLOCK, text);
}
if(is_set(action, A_UPDATE_NODESTATUS)) {
crm_log_maybe(log_level,
"Action %.16llx (A_UPDATE_NODESTATUS) %s",
A_UPDATE_NODESTATUS, text);
}
if(is_set(action, A_LOG)) {
crm_log_maybe(log_level,
"Action %.16llx (A_LOG ) %s", A_LOG, text);
}
if(is_set(action, A_ERROR)) {
crm_log_maybe(log_level,
"Action %.16llx (A_ERROR ) %s", A_ERROR, text);
}
if(is_set(action, A_WARN)) {
crm_log_maybe(log_level,
"Action %.16llx (A_WARN ) %s", A_WARN, text);
}
}
void
create_node_entry(const char *uuid, const char *uname, const char *type)
{
/* make sure a node entry exists for the new node
*
* this will add anyone except the first ever node in the cluster
* since it will also be the DC which doesnt go through the
* join process (with itself). We can include a special case
* later if desired.
*/
crm_data_t *tmp1 = create_xml_node(NULL, XML_CIB_TAG_NODE);
crm_debug_3("Creating node entry for %s", uname);
set_uuid(fsa_cluster_conn, tmp1, XML_ATTR_UUID, uname);
crm_xml_add(tmp1, XML_ATTR_UNAME, uname);
crm_xml_add(tmp1, XML_ATTR_TYPE, type);
fsa_cib_anon_update(XML_CIB_TAG_NODES, tmp1,
cib_scope_local|cib_quorum_override);
free_xml(tmp1);
}
struct crmd_ccm_data_s *
copy_ccm_data(const struct crmd_ccm_data_s *ccm_input)
{
const oc_ev_membership_t *oc_in =
(const oc_ev_membership_t *)ccm_input->oc;
struct crmd_ccm_data_s *ccm_input_copy = NULL;
crm_malloc0(ccm_input_copy, sizeof(struct crmd_ccm_data_s));
ccm_input_copy->oc = copy_ccm_oc_data(oc_in);
ccm_input_copy->event = ccm_input->event;
return ccm_input_copy;
}
oc_ev_membership_t *
copy_ccm_oc_data(const oc_ev_membership_t *oc_in)
{
unsigned lpc = 0;
int size = 0;
int offset = 0;
unsigned num_nodes = 0;
oc_ev_membership_t *oc_copy = NULL;
if(oc_in->m_n_member > 0
&& num_nodes < oc_in->m_n_member + oc_in->m_memb_idx) {
num_nodes = oc_in->m_n_member + oc_in->m_memb_idx;
crm_debug_3("Updated ccm nodes to %d - 1", num_nodes);
}
if(oc_in->m_n_in > 0
&& num_nodes < oc_in->m_n_in + oc_in->m_in_idx) {
num_nodes = oc_in->m_n_in + oc_in->m_in_idx;
crm_debug_3("Updated ccm nodes to %d - 2", num_nodes);
}
if(oc_in->m_n_out > 0
&& num_nodes < oc_in->m_n_out + oc_in->m_out_idx) {
num_nodes = oc_in->m_n_out + oc_in->m_out_idx;
crm_debug_3("Updated ccm nodes to %d - 3", num_nodes);
}
/* why 2*??
* ccm code does it like this so i guess its right...
*/
size = sizeof(oc_ev_membership_t)
+ sizeof(int)
+ 2*num_nodes*sizeof(oc_node_t);
crm_debug_3("Copying %d ccm nodes", num_nodes);
crm_malloc0(oc_copy, size);
oc_copy->m_instance = oc_in->m_instance;
oc_copy->m_n_member = oc_in->m_n_member;
oc_copy->m_memb_idx = oc_in->m_memb_idx;
oc_copy->m_n_out = oc_in->m_n_out;
oc_copy->m_out_idx = oc_in->m_out_idx;
oc_copy->m_n_in = oc_in->m_n_in;
oc_copy->m_in_idx = oc_in->m_in_idx;
crm_debug_3("instance=%d, nodes=%d (idx=%d), new=%d (idx=%d), lost=%d (idx=%d)",
oc_in->m_instance,
oc_in->m_n_member,
oc_in->m_memb_idx,
oc_in->m_n_in,
oc_in->m_in_idx,
oc_in->m_n_out,
oc_in->m_out_idx);
offset = oc_in->m_memb_idx;
for(lpc = 0; lpc < oc_in->m_n_member; lpc++) {
oc_node_t a_node = oc_in->m_array[lpc+offset];
oc_node_t *a_node_copy = &(oc_copy->m_array[lpc+offset]);
crm_debug_3("Copying ccm member node %d", lpc);
copy_ccm_node(a_node, a_node_copy);
}
offset = oc_in->m_in_idx;
for(lpc = 0; lpc < oc_in->m_n_in; lpc++) {
oc_node_t a_node = oc_in->m_array[lpc+offset];
oc_node_t *a_node_copy = &(oc_copy->m_array[lpc+offset]);
crm_debug_3("Copying ccm new node %d", lpc);
copy_ccm_node(a_node, a_node_copy);
}
offset = oc_in->m_out_idx;
for(lpc = 0; lpc < oc_in->m_n_out; lpc++) {
oc_node_t a_node = oc_in->m_array[lpc+offset];
oc_node_t *a_node_copy = &(oc_copy->m_array[lpc+offset]);
crm_debug_3("Copying ccm lost node %d", lpc);
copy_ccm_node(a_node, a_node_copy);
}
return oc_copy;
}
void
copy_ccm_node(oc_node_t a_node, oc_node_t *a_node_copy)
{
crm_debug_3("Copying ccm node: id=%d, born=%d, uname=%s",
a_node.node_id, a_node.node_born_on,
a_node.node_uname);
a_node_copy->node_id = a_node.node_id;
a_node_copy->node_born_on = a_node.node_born_on;
a_node_copy->node_uname = NULL;
if(a_node.node_uname != NULL) {
a_node_copy->node_uname =
crm_strdup(a_node.node_uname);
} else {
crm_err("Node Id %d had a NULL uname!",
a_node.node_id);
}
crm_debug_3("Copied ccm node: id=%d, born=%d, uname=%s",
a_node_copy->node_id, a_node_copy->node_born_on,
a_node_copy->node_uname);
}
crm_data_t*
create_node_state(
const char *uname, const char *ha_state, const char *ccm_state,
const char *crmd_state, const char *join_state, const char *exp_state,
gboolean clear_shutdown, const char *src)
{
crm_data_t *node_state = create_xml_node(NULL, XML_CIB_TAG_STATE);
crm_debug_2("%s Creating node state entry for %s", src, uname);
set_uuid(fsa_cluster_conn, node_state, XML_ATTR_UUID, uname);
+ if(ID(node_state) == NULL) {
+ crm_debug("Node %s is not a cluster member", uname);
+ free_xml(node_state);
+ return NULL;
+ }
+
crm_xml_add(node_state, XML_ATTR_UNAME, uname);
crm_xml_add(node_state, XML_CIB_ATTR_HASTATE, ha_state);
crm_xml_add(node_state, XML_CIB_ATTR_INCCM, ccm_state);
crm_xml_add(node_state, XML_CIB_ATTR_CRMDSTATE, crmd_state);
crm_xml_add(node_state, XML_CIB_ATTR_JOINSTATE, join_state);
crm_xml_add(node_state, XML_CIB_ATTR_EXPSTATE, exp_state);
crm_xml_add(node_state, XML_ATTR_ORIGIN, src);
if(clear_shutdown) {
crm_xml_add(node_state, XML_CIB_ATTR_SHUTDOWN, "0");
#if CRM_DEPRECATED_SINCE_2_0_3
crm_xml_add(node_state, "clear_shutdown", "true");
#endif
/* crm_xml_add(node_state, */
/* XML_CIB_ATTR_REPLACE, XML_TAG_TRANSIENT_NODEATTRS); */
}
crm_log_xml_debug_3(node_state, "created");
return node_state;
}
gboolean
need_transition(enum crmd_fsa_state state)
{
if(state == S_POLICY_ENGINE
|| state == S_TRANSITION_ENGINE
|| state == S_IDLE) {
return TRUE;
}
return FALSE;
}
extern GHashTable *ipc_clients;
void
process_client_disconnect(crmd_client_t *curr_client)
{
struct crm_subsystem_s *the_subsystem = NULL;
CRM_CHECK(curr_client != NULL, return);
crm_debug_2("received HUP from %s", curr_client->table_key);
if (curr_client->sub_sys == NULL) {
crm_debug_2("Client hadn't registered with us yet");
} else if (strcasecmp(CRM_SYSTEM_PENGINE, curr_client->sub_sys) == 0) {
the_subsystem = pe_subsystem;
} else if (strcasecmp(CRM_SYSTEM_TENGINE, curr_client->sub_sys) == 0) {
the_subsystem = te_subsystem;
} else if (strcasecmp(CRM_SYSTEM_CIB, curr_client->sub_sys) == 0){
the_subsystem = cib_subsystem;
}
if(the_subsystem != NULL) {
the_subsystem->ipc = NULL;
crm_info("Received HUP from %s:[%d]",
the_subsystem->name, the_subsystem->pid);
} else {
/* else that was a transient client */
crm_debug("Received HUP from transient client");
}
if (curr_client->table_key != NULL) {
/*
* Key is destroyed below:
* curr_client->table_key
* Value is cleaned up by:
* G_main_del_IPC_Channel
*/
g_hash_table_remove(ipc_clients, curr_client->table_key);
}
}
void update_dc(HA_Message *msg, gboolean assert_same)
{
const char *dc_version = NULL;
const char *welcome_from = NULL;
if(msg != NULL) {
dc_version = cl_get_string(msg, F_CRM_VERSION);
welcome_from = cl_get_string(msg, F_CRM_HOST_FROM);
CRM_CHECK(dc_version != NULL, return);
CRM_CHECK(welcome_from != NULL, return);
if(AM_I_DC) {
CRM_CHECK(safe_str_eq(welcome_from, fsa_our_uname),
return);
}
if(assert_same) {
CRM_CHECK(fsa_our_dc != NULL, ;);
CRM_CHECK(safe_str_eq(fsa_our_dc, welcome_from), ;);
}
}
crm_free(fsa_our_dc);
crm_free(fsa_our_dc_version);
fsa_our_dc = NULL;
fsa_our_dc_version = NULL;
if(welcome_from != NULL) {
fsa_our_dc = crm_strdup(welcome_from);
}
if(dc_version != NULL) {
fsa_our_dc_version = crm_strdup(dc_version);
}
crm_info("Set DC to %s (%s)",
crm_str(fsa_our_dc), crm_str(fsa_our_dc_version));
}
diff --git a/crm/tengine/actions.c b/crm/tengine/actions.c
index 1fea291455..2a9b53cfcf 100644
--- a/crm/tengine/actions.c
+++ b/crm/tengine/actions.c
@@ -1,495 +1,495 @@
/* $Id: actions.c,v 1.37 2006/08/14 09:14:45 andrew Exp $ */
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <portability.h>
#include <sys/param.h>
#include <crm/crm.h>
#include <crm/cib.h>
#include <crm/msg_xml.h>
#include <crm/common/msg.h>
#include <crm/common/xml.h>
#include <tengine.h>
#include <heartbeat.h>
#include <clplumbing/Gmain_timeout.h>
#include <lrm/lrm_api.h>
#include <clplumbing/lsb_exitcodes.h>
char *te_uuid = NULL;
IPC_Channel *crm_ch = NULL;
void send_rsc_command(crm_action_t *action);
extern crm_action_timer_t *transition_timer;
static void
te_start_action_timer(crm_action_t *action)
{
crm_malloc0(action->timer, sizeof(crm_action_timer_t));
action->timer->timeout = action->timeout;
action->timer->reason = timeout_action_warn;
action->timer->action = action;
action->timer->source_id = Gmain_timeout_add(
action->timer->timeout,
action_timer_callback, (void*)action->timer);
CRM_ASSERT(action->timer->source_id != 0);
}
static gboolean
te_pseudo_action(crm_graph_t *graph, crm_action_t *pseudo)
{
crm_info("Pseudo action %d fired and confirmed", pseudo->id);
pseudo->confirmed = TRUE;
update_graph(graph, pseudo);
trigger_graph();
return TRUE;
}
void
send_stonith_update(stonith_ops_t * op)
{
enum cib_errors rc = cib_ok;
const char *target = op->node_name;
const char *uuid = op->node_uuid;
/* zero out the node-status & remove all LRM status info */
crm_data_t *node_state = create_xml_node(NULL, XML_CIB_TAG_STATE);
CRM_CHECK(op->node_name != NULL, return);
CRM_CHECK(op->node_uuid != NULL, return);
crm_xml_add(node_state, XML_ATTR_UUID, uuid);
crm_xml_add(node_state, XML_ATTR_UNAME, target);
crm_xml_add(node_state, XML_CIB_ATTR_HASTATE, DEADSTATUS);
crm_xml_add(node_state, XML_CIB_ATTR_INCCM, XML_BOOLEAN_NO);
crm_xml_add(node_state, XML_CIB_ATTR_CRMDSTATE, OFFLINESTATUS);
crm_xml_add(node_state, XML_CIB_ATTR_JOINSTATE, CRMD_JOINSTATE_DOWN);
crm_xml_add(node_state, XML_CIB_ATTR_EXPSTATE, CRMD_JOINSTATE_DOWN);
crm_xml_add(node_state, XML_CIB_ATTR_REPLACE, XML_CIB_TAG_LRM);
crm_xml_add(node_state, XML_ATTR_ORIGIN, __FUNCTION__);
rc = te_cib_conn->cmds->update(
te_cib_conn, XML_CIB_TAG_STATUS, node_state, NULL,
cib_quorum_override|cib_scope_local);
if(rc < cib_ok) {
crm_err("CIB update failed: %s", cib_error2string(rc));
abort_transition(
INFINITY, tg_shutdown, "CIB update failed", node_state);
} else {
/* delay processing the trigger until the update completes */
add_cib_op_callback(rc, FALSE, NULL, cib_fencing_updated);
}
free_xml(node_state);
return;
}
static gboolean
te_fence_node(crm_graph_t *graph, crm_action_t *action)
{
char *key = NULL;
const char *id = NULL;
const char *uuid = NULL;
const char *target = NULL;
const char *type = NULL;
stonith_ops_t * st_op = NULL;
id = ID(action->xml);
target = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
uuid = crm_element_value(action->xml, XML_LRM_ATTR_TARGET_UUID);
type = g_hash_table_lookup(action->params, crm_meta_name("stonith_action"));
CRM_CHECK(id != NULL,
crm_log_xml_warn(action->xml, "BadAction");
return FALSE);
CRM_CHECK(uuid != NULL,
crm_log_xml_warn(action->xml, "BadAction");
return FALSE);
CRM_CHECK(type != NULL,
crm_log_xml_warn(action->xml, "BadAction");
return FALSE);
CRM_CHECK(target != NULL,
crm_log_xml_warn(action->xml, "BadAction");
return FALSE);
te_log_action(LOG_INFO,
"Executing %s fencing operation (%s) on %s (timeout=%d)",
type, id, target,
transition_graph->transition_timeout / 2);
crm_malloc0(st_op, sizeof(stonith_ops_t));
if(safe_str_eq(type, "poweroff")) {
st_op->optype = POWEROFF;
} else {
st_op->optype = RESET;
}
st_op->timeout = transition_graph->transition_timeout / 2;
st_op->node_name = crm_strdup(target);
st_op->node_uuid = crm_strdup(uuid);
key = generate_transition_key(transition_graph->id, te_uuid);
st_op->private_data = crm_concat(id, key, ';');
crm_free(key);
CRM_ASSERT(stonithd_input_IPC_channel() != NULL);
if (ST_OK != stonithd_node_fence( st_op )) {
crm_err("Cannot fence %s: stonithd_node_fence() call failed ",
target);
}
return TRUE;
}
static gboolean
te_crm_command(crm_graph_t *graph, crm_action_t *action)
{
char *value = NULL;
char *counter = NULL;
HA_Message *cmd = NULL;
const char *id = NULL;
const char *task = NULL;
const char *on_node = NULL;
gboolean ret = TRUE;
id = ID(action->xml);
task = crm_element_value(action->xml, XML_LRM_ATTR_TASK);
on_node = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
CRM_CHECK(on_node != NULL && strlen(on_node) != 0,
te_log_action(LOG_ERR, "Corrupted command (id=%s) %s: no node",
crm_str(id), crm_str(task));
return FALSE);
te_log_action(LOG_INFO, "Executing crm-event (%s): %s on %s",
crm_str(id), crm_str(task), on_node);
cmd = create_request(task, NULL, on_node, CRM_SYSTEM_CRMD,
CRM_SYSTEM_TENGINE, NULL);
counter = generate_transition_key(transition_graph->id, te_uuid);
crm_xml_add(cmd, XML_ATTR_TRANSITION_KEY, counter);
ret = send_ipc_message(crm_ch, cmd);
crm_free(counter);
crm_msg_del(cmd);
value = g_hash_table_lookup(action->params, crm_meta_name(XML_ATTR_TE_NOWAIT));
if(ret == FALSE) {
crm_err("Action %d failed: send", action->id);
return FALSE;
} else if(crm_is_true(value)) {
crm_info("Skipping wait for %d", action->id);
action->confirmed = TRUE;
update_graph(graph, action);
trigger_graph();
} else if(ret && action->timeout > 0) {
crm_debug("Setting timer for action %d",action->id);
action->timer->reason = timeout_action_warn;
te_start_action_timer(action);
}
return TRUE;
}
static gboolean
te_rsc_command(crm_graph_t *graph, crm_action_t *action)
{
/* never overwrite stop actions in the CIB with
* anything other than completed results
*
* Writing pending stops makes it look like the
* resource is running again
*/
const char *task = NULL;
const char *on_node = NULL;
action->executed = FALSE;
on_node = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
CRM_CHECK(on_node != NULL && strlen(on_node) != 0,
te_log_action(LOG_ERR, "Corrupted command(id=%s) %s: no node",
ID(action->xml), crm_str(task));
return FALSE);
send_rsc_command(action);
return TRUE;
}
gboolean
cib_action_update(crm_action_t *action, int status)
{
char *code = NULL;
char *digest = NULL;
crm_data_t *params = NULL;
crm_data_t *state = NULL;
crm_data_t *rsc = NULL;
crm_data_t *xml_op = NULL;
crm_data_t *action_rsc = NULL;
char *op_id = NULL;
enum cib_errors rc = cib_ok;
const char *name = NULL;
const char *value = NULL;
const char *rsc_id = NULL;
const char *task = crm_element_value(action->xml, XML_LRM_ATTR_TASK);
const char *target = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
const char *task_uuid = crm_element_value(
action->xml, XML_LRM_ATTR_TASK_KEY);
const char *target_uuid = crm_element_value(
action->xml, XML_LRM_ATTR_TARGET_UUID);
int call_options = cib_quorum_override|cib_scope_local;
crm_warn("%s %d: %s on %s timed out",
crm_element_name(action->xml), action->id, task_uuid, target);
action_rsc = find_xml_node(action->xml, XML_CIB_TAG_RESOURCE, TRUE);
if(action_rsc == NULL) {
return FALSE;
}
rsc_id = ID(action_rsc);
CRM_CHECK(rsc_id != NULL,
crm_log_xml_err(action->xml, "Bad:action");
return FALSE);
code = crm_itoa(status);
/*
update the CIB
<node_state id="hadev">
<lrm>
<lrm_resources>
<lrm_resource id="rsc2" last_op="start" op_code="0" target="hadev"/>
*/
state = create_xml_node(NULL, XML_CIB_TAG_STATE);
crm_xml_add(state, XML_ATTR_UUID, target_uuid);
crm_xml_add(state, XML_ATTR_UNAME, target);
rsc = create_xml_node(state, XML_CIB_TAG_LRM);
crm_xml_add(rsc, XML_ATTR_ID, target_uuid);
rsc = create_xml_node(rsc, XML_LRM_TAG_RESOURCES);
rsc = create_xml_node(rsc, XML_LRM_TAG_RESOURCE);
crm_xml_add(rsc, XML_ATTR_ID, rsc_id);
name = XML_ATTR_TYPE;
value = crm_element_value(action_rsc, name);
crm_xml_add(rsc, name, value);
name = XML_AGENT_ATTR_CLASS;
value = crm_element_value(action_rsc, name);
crm_xml_add(rsc, name, value);
name = XML_AGENT_ATTR_PROVIDER;
value = crm_element_value(action_rsc, name);
crm_xml_add(rsc, name, value);
xml_op = create_xml_node(rsc, XML_LRM_TAG_RSC_OP);
crm_xml_add(xml_op, XML_ATTR_ID, task);
op_id = generate_op_key(rsc_id, task, action->interval);
crm_xml_add(xml_op, XML_ATTR_ID, op_id);
crm_free(op_id);
crm_xml_add(xml_op, XML_LRM_ATTR_TASK, task);
crm_xml_add(xml_op, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
crm_xml_add(xml_op, XML_LRM_ATTR_OPSTATUS, code);
crm_xml_add(xml_op, XML_LRM_ATTR_CALLID, "-1");
crm_xml_add_int(xml_op, XML_LRM_ATTR_INTERVAL, action->interval);
crm_xml_add(xml_op, XML_LRM_ATTR_RC, code);
crm_xml_add(xml_op, XML_ATTR_ORIGIN, __FUNCTION__);
crm_free(code);
code = generate_transition_key(transition_graph->id, te_uuid);
crm_xml_add(xml_op, XML_ATTR_TRANSITION_KEY, code);
crm_free(code);
code = generate_transition_magic(
crm_element_value(xml_op, XML_ATTR_TRANSITION_KEY), status, status);
crm_xml_add(xml_op, XML_ATTR_TRANSITION_MAGIC, code);
crm_free(code);
params = find_xml_node(action->xml, "attributes", TRUE);
params = copy_xml(params);
filter_action_parameters(params, CRM_FEATURE_SET);
digest = calculate_xml_digest(params, TRUE);
crm_xml_add(xml_op, XML_LRM_ATTR_OP_DIGEST, digest);
crm_free(digest);
free_xml(params);
crm_debug_3("Updating CIB with \"%s\" (%s): %s %s on %s",
status<0?"new action":XML_ATTR_TIMEOUT,
crm_element_name(action->xml), crm_str(task), rsc_id, target);
rc = te_cib_conn->cmds->update(
te_cib_conn, XML_CIB_TAG_STATUS, state, NULL, call_options);
crm_debug("Updating CIB with %s action %d: %s %s on %s (call_id=%d)",
op_status2text(status), action->id, task_uuid, rsc_id, target, rc);
add_cib_op_callback(rc, FALSE, NULL, cib_action_updated);
free_xml(state);
action->sent_update = TRUE;
if(rc < cib_ok) {
return FALSE;
}
return TRUE;
}
void
send_rsc_command(crm_action_t *action)
{
HA_Message *cmd = NULL;
crm_data_t *rsc_op = NULL;
- char *counter = crm_itoa(transition_graph->id);
+ char *counter = NULL;
const char *task = NULL;
const char *value = NULL;
const char *on_node = NULL;
const char *task_uuid = NULL;
CRM_ASSERT(action != NULL);
CRM_ASSERT(action->xml != NULL);
rsc_op = action->xml;
task = crm_element_value(rsc_op, XML_LRM_ATTR_TASK);
task_uuid = crm_element_value(action->xml, XML_LRM_ATTR_TASK_KEY);
on_node = crm_element_value(rsc_op, XML_LRM_ATTR_TARGET);
counter = generate_transition_key(transition_graph->id, te_uuid);
crm_xml_add(rsc_op, XML_ATTR_TRANSITION_KEY, counter);
crm_info("Initiating action %d: %s on %s",
action->id, task_uuid, on_node);
crm_free(counter);
if(rsc_op != NULL) {
crm_log_xml_debug_2(rsc_op, "Performing");
}
cmd = create_request(CRM_OP_INVOKE_LRM, rsc_op, on_node,
CRM_SYSTEM_LRMD, CRM_SYSTEM_TENGINE, NULL);
#if 1
send_ipc_message(crm_ch, cmd);
#else
/* test the TE timer/recovery code */
if((action->id % 11) == 0) {
crm_err("Faking lost action %d: %s", action->id, task_uuid);
} else {
send_ipc_message(crm_ch, cmd);
}
#endif
crm_msg_del(cmd);
action->executed = TRUE;
value = g_hash_table_lookup(action->params, crm_meta_name(XML_ATTR_TE_NOWAIT));
if(crm_is_true(value)) {
crm_debug("Skipping wait for %d", action->id);
action->confirmed = TRUE;
update_graph(transition_graph, action);
trigger_graph();
} else if(action->timeout > 0) {
int action_timeout = 2 * action->timeout + transition_graph->network_delay;
crm_debug_3("Setting timer for action %s", task_uuid);
if(transition_graph->transition_timeout < action_timeout) {
crm_debug("Action %d:"
" Increasing transition %d timeout to %d",
action->id, transition_graph->id,
transition_graph->transition_timeout);
transition_graph->transition_timeout = action_timeout;
}
te_start_action_timer(action);
}
}
crm_graph_functions_t te_graph_fns = {
te_pseudo_action,
te_rsc_command,
te_crm_command,
te_fence_node
};
void
notify_crmd(crm_graph_t *graph)
{
HA_Message *cmd = NULL;
int log_level = LOG_DEBUG;
const char *op = CRM_OP_TEABORT;
int pending_callbacks = num_cib_op_callbacks();
stop_te_timer(transition_timer);
if(pending_callbacks != 0) {
crm_warn("Delaying completion until all CIB updates complete");
return;
}
CRM_CHECK(graph->complete, graph->complete = TRUE);
switch(graph->completion_action) {
case tg_stop:
op = CRM_OP_TECOMPLETE;
log_level = LOG_INFO;
break;
case tg_abort:
case tg_restart:
op = CRM_OP_TEABORT;
break;
case tg_shutdown:
crm_info("Exiting after transition");
exit(LSB_EXIT_OK);
}
te_log_action(log_level, "Transition %d status: %s - %s",
graph->id, op, crm_str(graph->abort_reason));
print_graph(LOG_DEBUG_3, graph);
cmd = create_request(
op, NULL, NULL, CRM_SYSTEM_DC, CRM_SYSTEM_TENGINE, NULL);
if(graph->abort_reason != NULL) {
ha_msg_add(cmd, "message", graph->abort_reason);
}
send_ipc_message(crm_ch, cmd);
crm_msg_del(cmd);
graph->abort_reason = NULL;
graph->completion_action = tg_restart;
}
diff --git a/crm/tengine/events.c b/crm/tengine/events.c
index 1341ac75c3..da847280e9 100644
--- a/crm/tengine/events.c
+++ b/crm/tengine/events.c
@@ -1,546 +1,551 @@
/* $Id: events.c,v 1.23 2006/08/14 09:14:45 andrew Exp $ */
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <portability.h>
#include <sys/param.h>
#include <crm/crm.h>
#include <crm/cib.h>
#include <crm/msg_xml.h>
#include <crm/common/msg.h>
#include <crm/common/xml.h>
#include <tengine.h>
#include <heartbeat.h>
#include <clplumbing/Gmain_timeout.h>
#include <lrm/lrm_api.h>
crm_data_t *need_abort(crm_data_t *update);
void process_graph_event(crm_data_t *event, const char *event_node);
int match_graph_event(
crm_action_t *action, crm_data_t *event, const char *event_node);
crm_data_t *
need_abort(crm_data_t *update)
{
crm_data_t *section_xml = NULL;
const char *section = NULL;
if(update == NULL) {
return NULL;
}
section = XML_CIB_TAG_NODES;
section_xml = get_object_root(section, update);
xml_child_iter(section_xml, child,
return section_xml;
);
section = XML_CIB_TAG_RESOURCES;
section_xml = get_object_root(section, update);
xml_child_iter(section_xml, child,
return section_xml;
);
section = XML_CIB_TAG_CONSTRAINTS;
section_xml = get_object_root(section, update);
xml_child_iter(section_xml, child,
return section_xml;
);
section = XML_CIB_TAG_CRMCONFIG;
section_xml = get_object_root(section, update);
xml_child_iter(section_xml, child,
return section_xml;
);
return NULL;
}
static gboolean
fail_incompletable_actions(crm_graph_t *graph, const char *down_node)
{
const char *target = NULL;
crm_data_t *last_action = NULL;
slist_iter(
synapse, synapse_t, graph->synapses, lpc,
if (synapse->confirmed) {
continue;
}
slist_iter(
action, crm_action_t, synapse->actions, lpc,
if(action->type == action_type_pseudo || action->confirmed) {
continue;
}
target = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
if(safe_str_eq(target, down_node)) {
action->failed = TRUE;
last_action = action->xml;
update_graph(graph, action);
crm_notice("Action %d (%s) is scheduled for %s (offline)",
action->id, ID(action->xml), down_node);
}
);
);
if(last_action != NULL) {
crm_warn("Node %s shutdown resulted in un-runnable actions", down_node);
abort_transition(INFINITY, tg_restart, "Node failure", last_action);
return TRUE;
}
return FALSE;
}
gboolean
extract_event(crm_data_t *msg)
{
int shutdown = 0;
const char *event_node = NULL;
/*
[cib fragment]
...
<status>
<node_state id="node1" state=CRMD_STATE_ACTIVE exp_state="active">
<lrm>
<lrm_resources>
<rsc_state id="" rsc_id="rsc4" node_id="node1" rsc_state="stopped"/>
*/
crm_debug_4("Extracting event from %s", crm_element_name(msg));
xml_child_iter_filter(
msg, node_state, XML_CIB_TAG_STATE,
crm_data_t *attrs = NULL;
crm_data_t *resources = NULL;
const char *ccm_state = crm_element_value(
node_state, XML_CIB_ATTR_INCCM);
const char *crmd_state = crm_element_value(
node_state, XML_CIB_ATTR_CRMDSTATE);
/* Transient node attribute changes... */
event_node = crm_element_value(node_state, XML_ATTR_ID);
crm_debug_2("Processing state update from %s", event_node);
crm_log_xml_debug_3(node_state, "Processing");
attrs = find_xml_node(
node_state, XML_TAG_TRANSIENT_NODEATTRS, FALSE);
if(attrs != NULL) {
crm_info("Aborting on "XML_TAG_TRANSIENT_NODEATTRS" changes for %s", event_node);
abort_transition(INFINITY, tg_restart,
XML_TAG_TRANSIENT_NODEATTRS, attrs);
}
resources = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE);
resources = find_xml_node(
resources, XML_LRM_TAG_RESOURCES, FALSE);
/* LRM resource update... */
xml_child_iter(
resources, rsc,
xml_child_iter(
rsc, rsc_op,
crm_log_xml_debug_3(rsc_op, "Processing resource update");
process_graph_event(rsc_op, event_node);
);
);
/*
* node state update... possibly from a shutdown we requested
*/
if(safe_str_eq(ccm_state, XML_BOOLEAN_FALSE)
|| safe_str_eq(crmd_state, CRMD_JOINSTATE_DOWN)) {
crm_action_t *shutdown = NULL;
shutdown = match_down_event(0, event_node, NULL);
if(shutdown != NULL) {
update_graph(transition_graph, shutdown);
trigger_graph();
} else {
crm_info("Stonith/shutdown of %s not matched", event_node);
abort_transition(INFINITY, tg_restart, "Node failure", node_state);
}
fail_incompletable_actions(transition_graph, event_node);
}
shutdown = 0;
ha_msg_value_int(node_state, XML_CIB_ATTR_SHUTDOWN, &shutdown);
if(shutdown != 0) {
crm_info("Aborting on "XML_CIB_ATTR_SHUTDOWN" attribute for %s", event_node);
abort_transition(INFINITY, tg_restart, "Shutdown request", node_state);
}
);
return TRUE;
}
static void
update_failcount(crm_action_t *action, int rc)
{
crm_data_t *rsc = NULL;
char *attr_name = NULL;
const char *task = NULL;
const char *rsc_id = NULL;
const char *on_node = NULL;
const char *on_uuid = NULL;
const char *interval = NULL;
if(rc == 99) {
/* this is an internal code for "we're busy, try again" */
return;
}
interval = g_hash_table_lookup(
action->params, crm_meta_name("interval"));
if(interval == NULL) {
return;
}
CRM_CHECK(action->xml != NULL, return);
rsc = find_xml_node(action->xml, XML_CIB_TAG_RESOURCE, TRUE);
CRM_CHECK(rsc != NULL, return);
rsc_id = ID(rsc);
CRM_CHECK(rsc_id != NULL, return);
task = crm_element_value(action->xml, XML_LRM_ATTR_TASK);
on_node = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
on_uuid = crm_element_value(action->xml, XML_LRM_ATTR_TARGET_UUID);
CRM_CHECK(task != NULL, return);
CRM_CHECK(on_uuid != NULL, return);
CRM_CHECK(on_node != NULL, return);
attr_name = crm_concat("fail-count", rsc_id, '-');
crm_warn("Updating failcount for %s on %s after failed %s: rc=%d",
rsc_id, on_node, task, rc);
update_attr(te_cib_conn, cib_none, XML_CIB_TAG_STATUS,
on_uuid, NULL,NULL, attr_name, XML_NVPAIR_ATTR_VALUE"++");
crm_free(attr_name);
}
/*
* returns the ID of the action if a match is found
* returns -1 if a match was not found
* returns -2 if a match was found but the action failed (and was
* not allowed to)
*/
int
match_graph_event(
crm_action_t *action, crm_data_t *event, const char *event_node)
{
int log_level_fail = LOG_ERR;
int target_rc = 0;
const char *target_rc_s = NULL;
const char *allow_fail = NULL;
const char *this_action = NULL;
const char *this_node = NULL;
const char *this_uname = NULL;
const char *magic = NULL;
const char *this_event;
char *update_te_uuid = NULL;
const char *update_event;
int op_status_i = -3;
int op_rc_i = -3;
int transition_i = -1;
CRM_CHECK(event != NULL, return -1);
crm_debug_3("Processing \"%s\" change", crm_element_name(event));
update_event = crm_element_value(event, XML_ATTR_ID);
magic = crm_element_value(event, XML_ATTR_TRANSITION_MAGIC);
CRM_CHECK(magic != NULL, return -2);
this_action = crm_element_value(action->xml, XML_LRM_ATTR_TASK);
this_uname = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
this_event = crm_element_value(action->xml, XML_LRM_ATTR_TASK_KEY);
this_node = crm_element_value(action->xml, XML_LRM_ATTR_TARGET_UUID);
CRM_CHECK(this_event != NULL, return -2);
if(safe_str_neq(this_event, update_event)) {
crm_debug_2("Action %d : Event mismatch %s vs. %s",
action->id, this_event, update_event);
return -1;
} else if(safe_str_neq(this_node, event_node)) {
crm_debug_2("Action %d : Node mismatch %s (%s) vs. %s",
action->id, this_node, this_uname, event_node);
return -1;
}
crm_debug_2("Matched action (%d) %s", action->id, this_event);
CRM_CHECK(decode_transition_magic(
magic, &update_te_uuid,
&transition_i, &op_status_i, &op_rc_i), return -2);
if(transition_i == -1) {
/* we never expect these - recompute */
crm_err("Detected action %s initiated outside of a transition",
this_event);
crm_log_message(LOG_ERR, event);
+ crm_free(update_te_uuid);
return -2;
} else if(safe_str_neq(update_te_uuid, te_uuid)) {
crm_info("Detected action %s from a different transitioner:"
" %s vs. %s", this_event, update_te_uuid, te_uuid);
crm_log_message(LOG_INFO, event);
+ crm_free(update_te_uuid);
return -3;
} else if(transition_graph->id != transition_i) {
crm_warn("Detected an action %s from a different transition:"
" %d vs. %d", this_event, transition_i,
transition_graph->id);
crm_log_message(LOG_INFO, event);
+ crm_free(update_te_uuid);
return -4;
}
+
+ crm_free(update_te_uuid);
/* stop this event's timer if it had one */
stop_te_timer(action->timer);
action->confirmed = TRUE;
target_rc_s = g_hash_table_lookup(
action->params,crm_meta_name(XML_ATTR_TE_TARGET_RC));
if(target_rc_s != NULL) {
crm_debug_2("Target rc: %s vs. %d", target_rc_s, op_rc_i);
target_rc = crm_parse_int(target_rc_s, NULL);
if(target_rc == op_rc_i) {
crm_debug_2("Target rc: == %d", op_rc_i);
if(op_status_i != LRM_OP_DONE) {
crm_debug_2("Re-mapping op status to"
" LRM_OP_DONE for %s",update_event);
op_status_i = LRM_OP_DONE;
}
} else {
crm_debug_2("Target rc: != %d", op_rc_i);
if(op_status_i != LRM_OP_ERROR) {
crm_info("Re-mapping op status to"
" LRM_OP_ERROR for %s", update_event);
op_status_i = LRM_OP_ERROR;
}
}
}
/* Process OP status */
switch(op_status_i) {
case -3:
crm_err("Action returned the same as last time..."
" whatever that was!");
crm_log_message(LOG_ERR, event);
break;
case LRM_OP_PENDING:
crm_debug("Ignoring pending operation");
return -5;
break;
case LRM_OP_DONE:
break;
case LRM_OP_ERROR:
/* This is the code we use for direct nack's */
if(op_rc_i == 99) {
log_level_fail = LOG_WARNING;
}
/* fall through */
case LRM_OP_TIMEOUT:
case LRM_OP_NOTSUPPORTED:
action->failed = TRUE;
crm_log_maybe(log_level_fail,
"Action %s on %s failed (target: %d vs. rc: %d): %s",
update_event, this_uname, target_rc,
op_rc_i, op_status2text(op_status_i));
break;
case LRM_OP_CANCELLED:
/* do nothing?? */
crm_err("Dont know what to do for cancelled ops yet");
break;
default:
action->failed = TRUE;
crm_err("Unsupported action result: %d", op_status_i);
}
update_graph(transition_graph, action);
trigger_graph();
if(action->failed) {
allow_fail = g_hash_table_lookup(
action->params, crm_meta_name(XML_ATTR_TE_ALLOWFAIL));
if(crm_is_true(allow_fail)) {
action->failed = FALSE;
}
}
if(action->failed) {
/* ignore probes */
if(target_rc != EXECRA_NOT_RUNNING) {
update_failcount(action, op_rc_i);
}
abort_transition(action->synapse->priority+1,
tg_restart, "Event failed", event);
} else if(transition_graph->complete) {
abort_transition(INFINITY, tg_restart,"No active graph", event);
}
te_log_action(LOG_INFO, "Action %s (%d) confirmed",
this_event, action->id);
return action->id;
}
crm_action_t *
match_down_event(int id, const char *target, const char *filter)
{
const char *this_action = NULL;
const char *this_node = NULL;
crm_action_t *match = NULL;
slist_iter(
synapse, synapse_t, transition_graph->synapses, lpc,
/* lookup event */
slist_iter(
action, crm_action_t, synapse->actions, lpc2,
if(id > 0 && action->id == id) {
match = action;
break;
}
this_action = crm_element_value(
action->xml, XML_LRM_ATTR_TASK);
if(action->type != action_type_crm) {
continue;
} else if(safe_str_eq(this_action, CRM_OP_LRM_REFRESH)){
continue;
} else if(filter != NULL
&& safe_str_neq(this_action, filter)) {
continue;
}
this_node = crm_element_value(
action->xml, XML_LRM_ATTR_TARGET_UUID);
if(this_node == NULL) {
crm_log_xml_err(action->xml, "No node uuid");
}
if(safe_str_neq(this_node, target)) {
crm_debug("Action %d : Node mismatch: %s",
action->id, this_node);
continue;
}
match = action;
break;
);
if(match != NULL) {
/* stop this event's timer if it had one */
break;
}
);
if(match != NULL) {
/* stop this event's timer if it had one */
crm_debug("Match found for action %d: %s on %s", id,
crm_element_value(match->xml, XML_LRM_ATTR_TASK_KEY),
target);
stop_te_timer(match->timer);
match->confirmed = TRUE;
} else if(id > 0) {
crm_err("No match for action %d", id);
} else {
crm_warn("No match for shutdown action on %s", target);
}
return match;
}
void
process_graph_event(crm_data_t *event, const char *event_node)
{
int rc = -1;
const char *magic = NULL;
const char *rsc_id = NULL;
CRM_ASSERT(event != NULL);
rsc_id = crm_element_value(event, XML_ATTR_ID);
magic = crm_element_value(event, XML_ATTR_TRANSITION_MAGIC);
if(magic == NULL) {
crm_log_xml_debug_2(event, "Skipping \"non-change\"");
return;
} else {
crm_debug_2("Processing CIB update: %s on %s: %s",
rsc_id, event_node, magic);
}
slist_iter(
synapse, synapse_t, transition_graph->synapses, lpc,
/* lookup event */
slist_iter(
action, crm_action_t, synapse->actions, lpc2,
rc = match_graph_event(action, event, event_node);
if(rc >= 0) {
crm_log_xml_debug_2(event, "match:found");
} else if(rc == -5) {
crm_log_xml_debug_2(event, "match:pending");
} else if(rc != -1) {
crm_warn("Search for %s terminated: %d",
ID(event), rc);
abort_transition(INFINITY, tg_restart,
"Unexpected event", event);
}
if(rc != -1) {
return;
}
);
);
/* unexpected event, trigger a pe-recompute */
/* possibly do this only for certain types of actions */
crm_warn("Event not found.");
crm_log_xml_info(event, "match:not-found");
abort_transition(INFINITY, tg_restart, "Unexpected event", event);
return;
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Oct 16, 3:26 PM (7 h, 4 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2536541
Default Alt Text
(82 KB)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment