blob: 71568dd3cc3ae383ca575ebb8b455122009014a2 [file] [log] [blame] [edit]
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* 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 <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/transition.h>
/* #include <sys/param.h> */
/* */
crm_graph_functions_t *graph_fns = NULL;
static gboolean
update_synapse_ready(synapse_t * synapse, int action_id)
{
GListPtr lpc = NULL;
gboolean updates = FALSE;
CRM_CHECK(synapse->executed == FALSE, return FALSE);
CRM_CHECK(synapse->confirmed == FALSE, return FALSE);
synapse->ready = TRUE;
for (lpc = synapse->inputs; lpc != NULL; lpc = lpc->next) {
crm_action_t *prereq = (crm_action_t *) lpc->data;
crm_trace("Processing input %d", prereq->id);
if (prereq->id == action_id) {
crm_trace("Marking input %d of synapse %d confirmed", action_id, synapse->id);
prereq->confirmed = TRUE;
updates = TRUE;
} else if (prereq->confirmed == FALSE) {
synapse->ready = FALSE;
}
}
if (updates) {
crm_trace("Updated synapse %d", synapse->id);
}
return updates;
}
static gboolean
update_synapse_confirmed(synapse_t * synapse, int action_id)
{
GListPtr lpc = NULL;
gboolean updates = FALSE;
gboolean is_confirmed = TRUE;
CRM_CHECK(synapse->executed, return FALSE);
CRM_CHECK(synapse->confirmed == FALSE, return TRUE);
is_confirmed = TRUE;
for (lpc = synapse->actions; lpc != NULL; lpc = lpc->next) {
crm_action_t *action = (crm_action_t *) lpc->data;
crm_trace("Processing action %d", action->id);
if (action->id == action_id) {
crm_trace("Confirmed: Action %d of Synapse %d", action_id, synapse->id);
action->confirmed = TRUE;
updates = TRUE;
} else if (action->confirmed == FALSE) {
is_confirmed = FALSE;
crm_trace("Synapse %d still not confirmed after action %d", synapse->id, action_id);
}
}
if (is_confirmed && synapse->confirmed == FALSE) {
crm_trace("Confirmed: Synapse %d", synapse->id);
synapse->confirmed = TRUE;
updates = TRUE;
}
if (updates) {
crm_trace("Updated synapse %d", synapse->id);
}
return updates;
}
gboolean
update_graph(crm_graph_t * graph, crm_action_t * action)
{
gboolean rc = FALSE;
gboolean updates = FALSE;
GListPtr lpc = NULL;
for (lpc = graph->synapses; lpc != NULL; lpc = lpc->next) {
synapse_t *synapse = (synapse_t *) lpc->data;
if (synapse->confirmed || synapse->failed) {
crm_trace("Synapse complete");
} else if (synapse->executed) {
crm_trace("Synapse executed");
rc = update_synapse_confirmed(synapse, action->id);
} else if (action->failed == FALSE || synapse->priority == INFINITY) {
rc = update_synapse_ready(synapse, action->id);
}
updates = updates || rc;
}
if (updates) {
crm_trace("Updated graph with completed action %d", action->id);
}
return updates;
}
static gboolean
should_fire_synapse(crm_graph_t * graph, synapse_t * synapse)
{
GListPtr lpc = NULL;
CRM_CHECK(synapse->executed == FALSE, return FALSE);
CRM_CHECK(synapse->confirmed == FALSE, return FALSE);
crm_trace("Checking pre-reqs for synapse %d", synapse->id);
/* lookup prereqs */
synapse->ready = TRUE;
for (lpc = synapse->inputs; lpc != NULL; lpc = lpc->next) {
crm_action_t *prereq = (crm_action_t *) lpc->data;
crm_trace("Processing input %d", prereq->id);
if (prereq->confirmed == FALSE) {
crm_trace("Input %d for synapse %d not satisfied: not confirmed", prereq->id, synapse->id);
synapse->ready = FALSE;
break;
} else if(prereq->failed && prereq->can_fail == FALSE) {
crm_trace("Input %d for synapse %d not satisfied: failed", prereq->id, synapse->id);
synapse->ready = FALSE;
break;
}
}
for (lpc = synapse->actions; synapse->ready && lpc != NULL; lpc = lpc->next) {
crm_action_t *a = (crm_action_t *) lpc->data;
if (a->type == action_type_pseudo) {
/* None of the below applies to pseudo ops */
} else if (synapse->priority < graph->abort_priority) {
crm_trace("Skipping synapse %d: abort level %d", synapse->id, graph->abort_priority);
graph->skipped++;
return FALSE;
} else if(graph_fns->allowed && graph_fns->allowed(graph, a) == FALSE) {
crm_trace("Deferring synapse %d: allowed", synapse->id);
return FALSE;
}
}
return synapse->ready;
}
static gboolean
initiate_action(crm_graph_t * graph, crm_action_t * action)
{
const char *id = NULL;
CRM_CHECK(action->executed == FALSE, return FALSE);
id = ID(action->xml);
CRM_CHECK(id != NULL, return FALSE);
action->executed = TRUE;
if (action->type == action_type_pseudo) {
crm_trace("Executing pseudo-event: %s (%d)", id, action->id);
return graph_fns->pseudo(graph, action);
} else if (action->type == action_type_rsc) {
crm_trace("Executing rsc-event: %s (%d)", id, action->id);
return graph_fns->rsc(graph, action);
} else if (action->type == action_type_crm) {
const char *task = NULL;
task = crm_element_value(action->xml, XML_LRM_ATTR_TASK);
CRM_CHECK(task != NULL, return FALSE);
if (safe_str_eq(task, CRM_OP_FENCE)) {
crm_trace("Executing STONITH-event: %s (%d)", id, action->id);
return graph_fns->stonith(graph, action);
}
crm_trace("Executing crm-event: %s (%d)", id, action->id);
return graph_fns->crmd(graph, action);
}
crm_err("Failed on unsupported command type: %s (id=%s)", crm_element_name(action->xml), id);
return FALSE;
}
static gboolean
fire_synapse(crm_graph_t * graph, synapse_t * synapse)
{
GListPtr lpc = NULL;
CRM_CHECK(synapse != NULL, return FALSE);
CRM_CHECK(synapse->ready, return FALSE);
CRM_CHECK(synapse->confirmed == FALSE, return TRUE);
crm_trace("Synapse %d fired", synapse->id);
synapse->executed = TRUE;
for (lpc = synapse->actions; lpc != NULL; lpc = lpc->next) {
crm_action_t *action = (crm_action_t *) lpc->data;
/* allow some leeway */
gboolean passed = FALSE;
/* Invoke the action and start the timer */
passed = initiate_action(graph, action);
if (passed == FALSE) {
crm_err("Failed initiating <%s id=%d> in synapse %d",
crm_element_name(action->xml), action->id, synapse->id);
synapse->confirmed = TRUE;
action->confirmed = TRUE;
action->failed = TRUE;
return FALSE;
}
}
return TRUE;
}
int
run_graph(crm_graph_t * graph)
{
GListPtr lpc = NULL;
int stat_log_level = LOG_DEBUG;
int pass_result = transition_active;
const char *status = "In-progress";
if (graph_fns == NULL) {
set_default_graph_functions();
}
if (graph == NULL) {
return transition_complete;
}
graph->fired = 0;
graph->pending = 0;
graph->skipped = 0;
graph->completed = 0;
graph->incomplete = 0;
crm_trace("Entering graph %d callback", graph->id);
/* Pre-calculate the number of completed and in-flight operations */
for (lpc = graph->synapses; lpc != NULL; lpc = lpc->next) {
synapse_t *synapse = (synapse_t *) lpc->data;
if (synapse->confirmed) {
crm_trace("Synapse %d complete", synapse->id);
graph->completed++;
} else if (synapse->failed == FALSE && synapse->executed) {
crm_trace("Synapse %d: confirmation pending", synapse->id);
graph->pending++;
}
}
/* Now check if there is work to do */
for (lpc = graph->synapses; lpc != NULL; lpc = lpc->next) {
synapse_t *synapse = (synapse_t *) lpc->data;
if (graph->batch_limit > 0 && graph->pending >= graph->batch_limit) {
crm_debug("Throttling output: batch limit (%d) reached", graph->batch_limit);
break;
} else if (synapse->failed) {
graph->skipped++;
continue;
} else if (synapse->confirmed || synapse->executed) {
/* Already handled */
continue;
}
if (should_fire_synapse(graph, synapse)) {
crm_trace("Synapse %d fired", synapse->id);
graph->fired++;
if(fire_synapse(graph, synapse) == FALSE) {
crm_err("Synapse %d failed to fire", synapse->id);
stat_log_level = LOG_ERR;
graph->abort_priority = INFINITY;
graph->incomplete++;
graph->fired--;
}
if (synapse->confirmed == FALSE) {
graph->pending++;
}
} else {
crm_trace("Synapse %d cannot fire", synapse->id);
graph->incomplete++;
}
}
if (graph->pending == 0 && graph->fired == 0) {
graph->complete = TRUE;
stat_log_level = LOG_NOTICE;
pass_result = transition_complete;
status = "Complete";
if (graph->incomplete != 0 && graph->abort_priority <= 0) {
stat_log_level = LOG_WARNING;
pass_result = transition_terminated;
status = "Terminated";
} else if (graph->skipped != 0) {
status = "Stopped";
}
} else if (graph->fired == 0) {
pass_result = transition_pending;
}
do_crm_log(stat_log_level,
"Transition %d (Complete=%d, Pending=%d,"
" Fired=%d, Skipped=%d, Incomplete=%d, Source=%s): %s",
graph->id, graph->completed, graph->pending, graph->fired,
graph->skipped, graph->incomplete, graph->source, status);
return pass_result;
}