Page MenuHomeClusterLabs Projects

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/crmd/lrm.c b/crmd/lrm.c
index 52c0271022..869a75f5cf 100644
--- a/crmd/lrm.c
+++ b/crmd/lrm.c
@@ -1,1985 +1,1985 @@
/*
* 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/cib.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;
int last_rc;
gboolean remove;
gboolean cancelled;
};
struct pending_deletion_op_s {
char *rsc;
ha_msg_input_t *input;
};
struct delete_event_s {
int rc;
const char *rsc;
};
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);
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 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);
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 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_inplace(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)
{
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);
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);
if (entry == NULL && rsc) {
entry = calloc(1, sizeof(rsc_history_t));
entry->id = crm_strdup(op->rsc_id);
g_hash_table_insert(resource_history, entry->id, entry);
entry->rsc.id = entry->id;
entry->rsc.type = crm_strdup(rsc->type);
entry->rsc.class = crm_strdup(rsc->class);
if (rsc->provider) {
entry->rsc.provider = crm_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;
}
}
/* 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) {
register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
return;
}
if (action & A_LRM_DISCONNECT) {
if (verify_stopped(cur_state, LOG_INFO) == FALSE) {
if(action == A_LRM_DISCONNECT) {
crmd_fsa_stall(NULL);
return;
}
}
if (is_set(fsa_input_register, R_LRM_CONNECTED)) {
clear_bit_inplace(fsa_input_register, R_LRM_CONNECTED);
fsa_lrm_conn->cmds->disconnect(fsa_lrm_conn);
crm_info("Disconnected from the LRM");
}
g_hash_table_destroy(resource_history);
resource_history = NULL;
g_hash_table_destroy(deletion_ops);
deletion_ops = NULL;
g_hash_table_destroy(pending_ops);
pending_ops = NULL;
}
if (action & A_LRM_CONNECT) {
int ret = lrmd_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);
if (ret != lrmd_ok) {
if (++num_lrm_register_fails < max_lrm_register_fails) {
crm_warn("Failed to sign on to the LRM %d"
" (%d max) times", num_lrm_register_fails, max_lrm_register_fails);
crm_timer_start(wait_timer);
crmd_fsa_stall(NULL);
return;
}
}
if (ret == lrmd_ok) {
crm_trace("LRM: set_lrm_callback...");
fsa_lrm_conn->cmds->set_callback(fsa_lrm_conn, lrm_op_callback);
}
if (ret != lrmd_ok) {
crm_err("Failed to sign on to the LRM %d" " (max) times", num_lrm_register_fails);
register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
return;
}
set_bit_inplace(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)
{
int counter = 0;
gboolean rc = TRUE;
GHashTableIter gIter;
rsc_history_t *entry = NULL;
crm_debug("Checking for active resources before exit");
if (cur_state == S_TERMINATE) {
log_level = LOG_ERR;
}
if (pending_ops) {
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(pending_ops, ghash_count_pending, &counter);
}
if (counter > 0) {
rc = FALSE;
do_crm_log(log_level,
"%d pending LRM operations at shutdown%s",
counter, cur_state == S_TERMINATE ? "" : "... waiting");
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);
}
goto bail;
}
if (resource_history == NULL) {
goto bail;
}
g_hash_table_iter_init(&gIter, resource_history);
while (g_hash_table_iter_next(&gIter, NULL, (void **)&entry)) {
if (is_rsc_active(entry->id) == FALSE) {
continue;
}
crm_err("Resource %s was active at shutdown."
" You may ignore this error if it is unmanaged.", entry->id);
g_hash_table_foreach(pending_ops, ghash_print_pending_for_rsc, entry->id);
}
bail:
set_bit_inplace(fsa_input_register, R_SENT_RSC_STOP);
if (cur_state == S_TERMINATE) {
rc = TRUE;
}
return rc;
}
static char *
get_rsc_metadata(const char *type, const char *class, const char *provider)
{
char *metadata = NULL;
CRM_CHECK(type != NULL, return NULL);
CRM_CHECK(class != 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);
if (metadata) {
/* copy the metadata because the LRM likes using
* g_alloc instead of cl_malloc
*/
char *m_copy = crm_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);
- slist_basic_destroy(reload->restart_list);
+ 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;
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 = crm_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;
gboolean non_empty = FALSE;
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) {
non_empty = TRUE;
crm_xml_add(restart, param, value);
}
len += strlen(param) + 2;
crm_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);
#if 0
crm_debug("%s: %s, %s", rsc->id, digest, list);
if (non_empty) {
crm_log_xml_debug(restart, "restart digest source");
}
#endif
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)
{
rsc_history_t *entry = NULL;
crm_trace("Processing lrmd_rsc_info_t entry %s", rsc_id);
entry = g_hash_table_lookup(resource_history, rsc_id);
if (entry == NULL || entry->last == NULL) {
return FALSE;
}
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)
{
GHashTableIter iter;
rsc_history_t *entry = NULL;
g_hash_table_iter_init(&iter, 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)
{
gboolean shut_down = FALSE;
xmlNode *xml_result = NULL;
xmlNode *xml_state = NULL;
xmlNode *xml_data = NULL;
xmlNode *rsc_list = NULL;
const char *exp_state = CRMD_STATE_ACTIVE;
if (is_set(fsa_input_register, R_SHUTDOWN)) {
exp_state = CRMD_STATE_INACTIVE;
shut_down = TRUE;
}
xml_state = create_node_state(fsa_our_uname, ACTIVESTATUS, XML_BOOLEAN_TRUE,
ONLINESTATUS, CRMD_JOINSTATE_MEMBER, exp_state,
!shut_down, __FUNCTION__);
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);
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;
}
static void
notify_deleted(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 == lrmd_ok ? "" : " not");
op = construct_op(input->xml, rsc_id, CRMD_ACTION_DELETE);
CRM_ASSERT(op != NULL);
if (rc == lrmd_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(fsa_cib_conn, cib_none, XML_CIB_TAG_CRMCONFIG,
NULL, NULL, NULL, NULL, "last-lrm-refresh", now_s, FALSE);
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);
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)
{
char *rsc_xpath = NULL;
int max = 0;
int rc = cib_ok;
- CRM_CHECK(rsc_id != NULL, return cib_id_check);
+ CRM_CHECK(rsc_id != NULL, return cib_NOTEXISTS);
max = strlen(rsc_template) + strlen(rsc_id) + strlen(fsa_our_uname) + 1;
rsc_xpath = calloc(1, max);
snprintf(rsc_xpath, max, rsc_template, fsa_our_uname, rsc_id);
rc = fsa_cib_conn->cmds->delegated_variant_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)
{
struct delete_event_s event;
CRM_CHECK(rsc_id != NULL, return);
if (rc == lrmd_ok) {
char *rsc_id_copy = crm_strdup(rsc_id);
if (rsc_gIter)
g_hash_table_iter_remove(rsc_gIter);
else
g_hash_table_remove(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);
g_hash_table_foreach_remove(pending_ops, lrm_remove_deleted_op, rsc_id_copy);
free(rsc_id_copy);
}
if (input) {
notify_deleted(input, rsc_id, rc);
}
event.rc = rc;
event.rsc = rsc_id;
g_hash_table_foreach_remove(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)
{
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) +
10;
op_xpath = calloc(1, max);
snprintf(op_xpath, max, op_call_template, fsa_our_uname, rsc_id, key, call_id);
} else {
max = strlen(op_template) + strlen(rsc_id) + strlen(fsa_our_uname) + strlen(key) + 1;
op_xpath = calloc(1, max);
snprintf(op_xpath, max, op_template, fsa_our_uname, 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;
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;
}
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;
}
}
}
static gboolean
cancel_op(const char *rsc_id, const char *key, int op, gboolean remove)
{
int rc = lrmd_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);
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,
pending->rsc_id,
pending->op_type,
pending->interval);
if (rc == lrmd_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;
};
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) {
return TRUE;
}
}
return FALSE;
}
static gboolean
cancel_op_key(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;
g_hash_table_foreach_remove(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)
{
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);
if (!rsc && long_id) {
rsc = fsa_lrm_conn->cmds->get_rsc_info(fsa_lrm_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,
id, class, provider, type, 0);
rsc = fsa_lrm_conn->cmds->get_rsc_info(fsa_lrm_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)
{
int rc = lrmd_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);
}
if (rc == lrmd_ok) {
crm_trace("Resource '%s' deleted", id);
} else if (rc == lrmd_pending) {
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 = crm_strdup(rsc->id);
op->input = copy_ha_msg_input(request);
g_hash_table_insert(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);
}
/* 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;
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;
#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_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");
free((char *) op->user_data);
op->user_data = NULL;
entry = g_hash_table_lookup(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);
if (rsc) {
crm_info("Failing resource %s...", rsc->id);
process_lrm_event(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)) {
enum cib_errors rc = cib_ok;
xmlNode *fragment = do_lrm_query(TRUE);
crm_info("Forcing a local LRM refresh");
fsa_cib_update(XML_CIB_TAG_STATUS, fragment, cib_quorum_override, rc, user_name);
free_xml(fragment);
} else if (safe_str_eq(crm_op, CRM_OP_LRM_QUERY)) {
xmlNode *data = do_lrm_query(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);
while (g_hash_table_iter_next(&gIter, NULL, (void **)&entry)) {
delete_resource(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);
/* 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);
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, lrmd_ok, user_name);
op = construct_op(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);
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);
} else {
/* the normal case when the PE cancels an orphan op */
done = cancel_op(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);
/* needed?? surely not otherwise the cancel_op_(_key) wouldn't
* have failed in the first place
*/
g_hash_table_remove(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 = cib_ok;
CRM_ASSERT(rsc != NULL);
cib_rc = delete_rsc_status(rsc->id, cib_dryrun | cib_sync_call, user_name);
if (cib_rc != cib_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,
cib_error2string(cib_rc));
op = construct_op(input->xml, rsc->id, operation);
op->op_status = PCMK_LRM_OP_ERROR;
if (cib_rc == cib_permission_denied) {
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);
} else if (rsc != NULL) {
do_lrm_rsc_op(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)
{
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 = crm_strdup(operation);
op->op_status = PCMK_LRM_OP_PENDING;
op->rc = -1;
op->rsc_id = crm_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,
crm_strdup(XML_ATTR_CRM_VERSION), crm_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);
/* 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 = crm_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_CHECK(op != NULL, return);
if (op->rsc_id == NULL) {
CRM_LOG_ASSERT(rsc_id != NULL);
op->rsc_id = crm_strdup(rsc_id);
}
if (to_sys == NULL) {
to_sys = CRM_SYSTEM_TENGINE;
}
update = create_node_state(fsa_our_uname, NULL, NULL, NULL, NULL, NULL, FALSE, __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);
}
static gboolean
stop_recurring_action_by_rsc(gpointer key, gpointer value, gpointer user_data)
{
lrmd_rsc_info_t *rsc = 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) {
return TRUE;
}
}
return FALSE;
}
static gboolean
stop_recurring_actions(gpointer key, gpointer value, gpointer user_data)
{
gboolean remove = FALSE;
struct recurring_op_s *op = (struct recurring_op_s *)value;
if (op->interval != 0) {
remove = cancel_op(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)
{
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);
/* 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);
}
/* 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);
}
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,
rsc->id,
op->op_type,
op->user_data,
op->interval,
op->timeout,
op->start_delay,
0,
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 = crm_strdup(operation);
pending->op_key = crm_strdup(op_id);
pending->rsc_id = crm_strdup(rsc->id);
pending->last_rc = -1; /* All rc are positive, -1 indicates the last rc has not been set. */
g_hash_table_replace(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;
}
static void
cib_rsc_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
{
switch (rc) {
case cib_ok:
case cib_diff_failed:
case cib_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, cib_error2string(rc));
}
}
static int
do_update_resource(lrmd_rsc_info_t * rsc, lrmd_event_data_t * op)
{
/*
<status>
<nodes_status id=uname>
<lrm>
<lrm_resources>
<lrm_resource id=...>
</...>
*/
int rc = cib_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);
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);
/* the return code is a call number, not an error code */
crm_trace("Sent resource state update message: %d", rc);
fsa_cib_conn->cmds->register_callback(fsa_cib_conn, rc, 60, FALSE, NULL, "cib_rsc_callback",
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)
{
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);
/* ignore recurring ops that have not changed. */
if (op->interval && pending && (pending->last_rc == op->rc)) {
free(op_id);
return TRUE;
}
if (pending) {
pending->last_rc = op->rc;
}
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);
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) {
update_id = do_update_resource(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);
} 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)) {
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->rc != 0 && op->output != NULL) {
crm_info("Result: %s", op->output);
} else if (op->output != NULL) {
crm_debug("Result: %s", op->output);
}
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, lrmd_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);
lrmd_free_rsc_info(rsc);
free(op_key);
free(op_id);
return TRUE;
}
diff --git a/lib/common/xml.c b/lib/common/xml.c
index d3cfe103a7..1a293c439d 100644
--- a/lib/common/xml.c
+++ b/lib/common/xml.c
@@ -1,2866 +1,2866 @@
/*
* 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 <sys/param.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <ctype.h>
#include <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <libxml/xmlreader.h>
#if HAVE_BZLIB_H
# include <bzlib.h>
#endif
#if HAVE_LIBXML2
# include <libxml/parser.h>
# include <libxml/tree.h>
# include <libxml/relaxng.h>
#endif
#if HAVE_LIBXSLT
# include <libxslt/xslt.h>
# include <libxslt/transform.h>
#endif
#define XML_BUFFER_SIZE 4096
#define XML_PARSER_DEBUG 0
#define BEST_EFFORT_STATUS 0
void xml_log(int priority, const char * fmt, ...) G_GNUC_PRINTF(2,3);
void xml_log(int priority, const char * fmt, ...)
{
va_list ap;
va_start(ap, fmt);
qb_log_from_external_source_va(__FUNCTION__, __FILE__, fmt, priority, __LINE__, 0, ap);
va_end(ap);
}
typedef struct
{
xmlRelaxNGPtr rng;
xmlRelaxNGValidCtxtPtr valid;
xmlRelaxNGParserCtxtPtr parser;
} relaxng_ctx_cache_t;
struct schema_s
{
int type;
const char *name;
const char *location;
const char *transform;
int after_transform;
void *cache;
};
struct schema_s known_schemas[] = {
/* 0 */ { 0, NULL, NULL, NULL, 1 },
/* 1 */ { 1, "pacemaker-0.6", "crm.dtd", "upgrade06.xsl", 4, NULL },
/* 2 */ { 1, "transitional-0.6", "crm-transitional.dtd", "upgrade06.xsl", 4, NULL },
/* 3 */ { 2, "pacemaker-0.7", "pacemaker-1.0.rng", NULL, 0, NULL },
/* 4 */ { 2, "pacemaker-1.0", "pacemaker-1.0.rng", NULL, 6, NULL },
/* 5 */ { 2, "pacemaker-1.1", "pacemaker-1.1.rng", NULL, 6, NULL },
/* 6 */ { 2, "pacemaker-1.2", "pacemaker-1.2.rng", NULL, 0, NULL },
/* 7 */ { 0, "none", NULL, NULL, 0, NULL },
};
static int all_schemas = DIMOF(known_schemas);
static int max_schemas = DIMOF(known_schemas) - 2; /* skip back past 'none' */
typedef struct
{
int found;
const char *string;
} filter_t;
static filter_t filter[] = {
{ 0, XML_ATTR_ORIGIN },
{ 0, XML_CIB_ATTR_WRITTEN },
{ 0, XML_ATTR_UPDATE_ORIG },
{ 0, XML_ATTR_UPDATE_CLIENT },
{ 0, XML_ATTR_UPDATE_USER },
};
static char *get_schema_path(const char *file)
{
static const char *base = NULL;
if(base == NULL) {
base = getenv("PCMK_schema_directory");
}
if(base == NULL || strlen(base) == 0) {
base = CRM_DTD_DIRECTORY;
}
return crm_concat(base, file, '/');
}
int print_spaces(char *buffer, int spaces, int max);
int get_tag_name(const char *input, size_t offset, size_t max);
int get_attr_name(const char *input, size_t offset, size_t max);
int get_attr_value(const char *input, size_t offset, size_t max);
gboolean can_prune_leaf(xmlNode *xml_node);
void diff_filter_context(int context, int upper_bound, int lower_bound,
xmlNode *xml_node, xmlNode *parent);
int in_upper_context(int depth, int context, xmlNode *xml_node);
int write_file(const char *string, const char *filename);
xmlNode *subtract_xml_object(xmlNode *parent, xmlNode *left, xmlNode *right, gboolean full, const char *marker);
int add_xml_object(xmlNode *parent, xmlNode *target, xmlNode *update, gboolean as_diff);
xmlNode *
find_xml_node(xmlNode *root, const char * search_path, gboolean must_find)
{
xmlNode *a_child = NULL;
const char *name = "NULL";
if(must_find || root != NULL) {
crm_validate_data(root);
}
if(root != NULL) {
name = crm_element_name(root);
}
if(search_path == NULL) {
crm_warn("Will never find <NULL>");
return NULL;
}
for(a_child = __xml_first_child(root); a_child != NULL; a_child = __xml_next(a_child)) {
if(crm_str_eq((const char *)a_child->name, search_path, TRUE)) {
/* crm_trace("returning node (%s).", crm_element_name(a_child)); */
crm_validate_data(a_child);
return a_child;
}
}
if(must_find) {
crm_warn("Could not find %s in %s.", search_path, name);
} else if(root != NULL) {
crm_trace("Could not find %s in %s.", search_path, name);
} else {
crm_trace("Could not find %s in <NULL>.", search_path);
}
return NULL;
}
xmlNode*
find_entity(xmlNode *parent, const char *node_name, const char *id)
{
xmlNode *a_child = NULL;
crm_validate_data(parent);
for(a_child = __xml_first_child(parent); a_child != NULL; a_child = __xml_next(a_child)) {
/* Uncertain if node_name == NULL check is strictly necessary here */
if(node_name == NULL || crm_str_eq((const char *)a_child->name, node_name, TRUE)) {
if(id == NULL || crm_str_eq(id, ID(a_child), TRUE)) {
crm_trace("returning node (%s).",
crm_element_name(a_child));
return a_child;
}
}
}
crm_trace("node <%s id=%s> not found in %s.",
node_name, id, crm_element_name(parent));
return NULL;
}
void
copy_in_properties(xmlNode* target, xmlNode *src)
{
crm_validate_data(src);
crm_validate_data(target);
if(src == NULL) {
crm_warn("No node to copy properties from");
} else if (target == NULL) {
crm_err("No node to copy properties into");
} else {
xml_prop_iter(
src, local_prop_name, local_prop_value,
expand_plus_plus(target, local_prop_name, local_prop_value)
);
crm_validate_data(target);
}
return;
}
void fix_plus_plus_recursive(xmlNode* target)
{
/* TODO: Remove recursion and use xpath searches for value++ */
xmlNode *child = NULL;
xml_prop_iter(target, name, value, expand_plus_plus(target, name, value));
for(child = __xml_first_child(target); child != NULL; child = __xml_next(child)) {
fix_plus_plus_recursive(child);
}
}
void
expand_plus_plus(xmlNode* target, const char *name, const char *value)
{
int offset = 1;
int name_len = 0;
int int_value = 0;
int value_len = 0;
const char *old_value = NULL;
if(value == NULL || name == NULL) {
return;
}
old_value = crm_element_value(target, name);
if(old_value == NULL) {
/* if no previous value, set unexpanded */
goto set_unexpanded;
} else if(strstr(value, name) != value) {
goto set_unexpanded;
}
name_len = strlen(name);
value_len = strlen(value);
if(value_len < (name_len + 2)
|| value[name_len] != '+'
|| (value[name_len+1] != '+' && value[name_len+1] != '=')) {
goto set_unexpanded;
}
/* if we are expanding ourselves,
* then no previous value was set and leave int_value as 0
*/
if(old_value != value) {
int_value = char2score(old_value);
}
if(value[name_len+1] != '+') {
const char *offset_s = value+(name_len+2);
offset = char2score(offset_s);
}
int_value += offset;
if(int_value > INFINITY) {
int_value = INFINITY;
}
crm_xml_add_int(target, name, int_value);
return;
set_unexpanded:
if(old_value == value) {
/* the old value is already set, nothing to do */
return;
}
crm_xml_add(target, name, value);
return;
}
xmlDoc *getDocPtr(xmlNode *node)
{
xmlDoc *doc = NULL;
CRM_CHECK(node != NULL, return NULL);
doc = node->doc;
if(doc == NULL) {
doc = xmlNewDoc((const xmlChar*)"1.0");
xmlDocSetRootElement(doc, node);
xmlSetTreeDoc(node, doc);
}
return doc;
}
xmlNode*
add_node_copy(xmlNode *parent, xmlNode *src_node)
{
xmlNode *child = NULL;
xmlDoc *doc = getDocPtr(parent);
CRM_CHECK(src_node != NULL, return NULL);
child = xmlDocCopyNode(src_node, doc, 1);
xmlAddChild(parent, child);
return child;
}
int
add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child)
{
add_node_copy(parent, child);
free_xml(child);
return 1;
}
const char *
crm_xml_add(xmlNode* node, const char *name, const char *value)
{
xmlAttr *attr = NULL;
CRM_CHECK(node != NULL, return NULL);
CRM_CHECK(name != NULL, return NULL);
if(value == NULL) {
return NULL;
}
#if XML_PARANOIA_CHECKS
{
const char *old_value = NULL;
old_value = crm_element_value(node, name);
/* Could be re-setting the same value */
CRM_CHECK(old_value != value,
crm_err("Cannot reset %s with crm_xml_add(%s)",
name, value);
return value);
}
#endif
attr = xmlSetProp(node, (const xmlChar*)name, (const xmlChar*)value);
CRM_CHECK(attr && attr->children && attr->children->content, return NULL);
return (char *)attr->children->content;
}
const char *
crm_xml_replace(xmlNode* node, const char *name, const char *value)
{
xmlAttr *attr = NULL;
const char *old_value = NULL;
CRM_CHECK(node != NULL, return NULL);
CRM_CHECK(name != NULL && name[0] != 0, return NULL);
old_value = crm_element_value(node, name);
/* Could be re-setting the same value */
CRM_CHECK(old_value != value, return value);
if (old_value != NULL && value == NULL) {
xml_remove_prop(node, name);
return NULL;
} else if(value == NULL) {
return NULL;
}
attr = xmlSetProp(node, (const xmlChar*)name, (const xmlChar*)value);
CRM_CHECK(attr && attr->children && attr->children->content, return NULL);
return (char *)attr->children->content;
}
const char *
crm_xml_add_int(xmlNode* node, const char *name, int value)
{
char *number = crm_itoa(value);
const char *added = crm_xml_add(node, name, number);
free(number);
return added;
}
xmlNode*
create_xml_node(xmlNode *parent, const char *name)
{
xmlDoc *doc = NULL;
xmlNode *node = NULL;
if (name == NULL || name[0] == 0) {
return NULL;
}
if(parent == NULL) {
doc = xmlNewDoc((const xmlChar*)"1.0");
node = xmlNewDocRawNode(doc, NULL, (const xmlChar*)name, NULL);
xmlDocSetRootElement(doc, node);
} else {
doc = getDocPtr(parent);
node = xmlNewDocRawNode(doc, NULL, (const xmlChar*)name, NULL);
xmlAddChild(parent, node);
}
return node;
}
void
free_xml_from_parent(xmlNode *parent, xmlNode *a_node)
{
CRM_CHECK(a_node != NULL, return);
xmlUnlinkNode(a_node);
xmlFreeNode(a_node);
}
xmlNode*
copy_xml(xmlNode *src)
{
xmlDoc *doc = xmlNewDoc((const xmlChar*)"1.0");
xmlNode *copy = xmlDocCopyNode(src, doc, 1);
xmlDocSetRootElement(doc, copy);
xmlSetTreeDoc(copy, doc);
return copy;
}
static void crm_xml_err(void * ctx, const char * msg, ...) G_GNUC_PRINTF(2,3);
int
write_file(const char *string, const char *filename)
{
int rc = 0;
FILE *file_output_strm = NULL;
CRM_CHECK(filename != NULL, return -1);
if (string == NULL) {
crm_err("Cannot write NULL to %s", filename);
return -1;
}
file_output_strm = fopen(filename, "w");
if(file_output_strm == NULL) {
crm_perror(LOG_ERR,"Cannot open %s for writing", filename);
return -1;
}
rc = fprintf(file_output_strm, "%s", string);
if(rc < 0) {
crm_perror(LOG_ERR,"Cannot write output to %s", filename);
}
if(fflush(file_output_strm) != 0) {
crm_perror(LOG_ERR,"fflush for %s failed:", filename);
rc = -1;
}
if(fsync(fileno(file_output_strm)) < 0) {
crm_perror(LOG_ERR,"fsync for %s failed:", filename);
rc = -1;
}
fclose(file_output_strm);
return rc;
}
static void crm_xml_err(void * ctx, const char * msg, ...)
{
int len = 0;
va_list args;
char *buf = NULL;
static int buffer_len = 0;
static char *buffer = NULL;
va_start(args, msg);
len = vasprintf(&buf, msg, args);
if(strchr(buf, '\n')) {
buf[len - 1] = 0;
if(buffer) {
crm_err("XML Error: %s%s", buffer, buf);
free(buffer);
} else {
crm_err("XML Error: %s", buf);
}
buffer = NULL;
buffer_len = 0;
} else if(buffer == NULL) {
buffer_len = len;
buffer = buf;
buf = NULL;
} else {
buffer = realloc(buffer, 1+buffer_len+len);
memcpy(buffer+buffer_len, buf, len);
buffer_len += len;
buffer[buffer_len] = 0;
}
va_end(args);
free(buf);
}
xmlNode*
string2xml(const char *input)
{
xmlNode *xml = NULL;
xmlDocPtr output = NULL;
xmlParserCtxtPtr ctxt = NULL;
xmlErrorPtr last_error = NULL;
if(input == NULL) {
crm_err("Can't parse NULL input");
return NULL;
}
/* create a parser context */
ctxt = xmlNewParserCtxt();
CRM_CHECK(ctxt != NULL, return NULL);
/* xmlCtxtUseOptions(ctxt, XML_PARSE_NOBLANKS|XML_PARSE_RECOVER); */
xmlCtxtResetLastError(ctxt);
xmlSetGenericErrorFunc(ctxt, crm_xml_err);
/* initGenericErrorDefaultFunc(crm_xml_err); */
output = xmlCtxtReadDoc(ctxt, (const xmlChar*)input, NULL, NULL, XML_PARSE_NOBLANKS|XML_PARSE_RECOVER);
if(output) {
xml = xmlDocGetRootElement(output);
}
last_error = xmlCtxtGetLastError(ctxt);
if(last_error && last_error->code != XML_ERR_OK) {
/* crm_abort(__FILE__,__PRETTY_FUNCTION__,__LINE__, "last_error->code != XML_ERR_OK", TRUE, TRUE); */
/*
* http://xmlsoft.org/html/libxml-xmlerror.html#xmlErrorLevel
* http://xmlsoft.org/html/libxml-xmlerror.html#xmlParserErrors
*/
crm_warn("Parsing failed (domain=%d, level=%d, code=%d): %s",
last_error->domain, last_error->level,
last_error->code, last_error->message);
if(last_error->code != XML_ERR_DOCUMENT_END) {
crm_err("Couldn't%s parse %d chars: %s", xml?" fully":"", (int)strlen(input), input);
if(xml != NULL) {
crm_log_xml_err(xml, "Partial");
}
} else {
int len = strlen(input);
crm_warn("String start: %.50s", input);
crm_warn("String start+%d: %s", len-50, input+len-50);
crm_abort(__FILE__,__PRETTY_FUNCTION__,__LINE__, "String parsing error", TRUE, TRUE);
}
}
xmlFreeParserCtxt(ctxt);
return xml;
}
xmlNode *
stdin2xml(void)
{
size_t data_length = 0;
size_t read_chars = 0;
char *xml_buffer = NULL;
xmlNode *xml_obj = NULL;
do {
crm_realloc(xml_buffer, XML_BUFFER_SIZE + data_length + 1);
read_chars = fread(xml_buffer + data_length, 1, XML_BUFFER_SIZE, stdin);
data_length += read_chars;
} while (read_chars > 0);
if(data_length == 0) {
crm_warn("No XML supplied on stdin");
free(xml_buffer);
return NULL;
}
xml_buffer[data_length] = '\0';
xml_obj = string2xml(xml_buffer);
free(xml_buffer);
crm_log_xml_trace(xml_obj, "Created fragment");
return xml_obj;
}
static char *
decompress_file(const char *filename)
{
char *buffer = NULL;
#if HAVE_BZLIB_H
int rc = 0;
size_t length = 0, read_len = 0;
BZFILE *bz_file = NULL;
FILE *input = fopen(filename, "r");
if(input == NULL) {
crm_perror(LOG_ERR,"Could not open %s for reading", filename);
return NULL;
}
bz_file = BZ2_bzReadOpen(&rc, input, 0, 0, NULL, 0);
if ( rc != BZ_OK ) {
BZ2_bzReadClose ( &rc, bz_file);
return NULL;
}
rc = BZ_OK;
while ( rc == BZ_OK ) {
crm_realloc(buffer, XML_BUFFER_SIZE + length + 1);
read_len = BZ2_bzRead (
&rc, bz_file, buffer + length, XML_BUFFER_SIZE);
crm_trace("Read %ld bytes from file: %d",
(long)read_len, rc);
if ( rc == BZ_OK || rc == BZ_STREAM_END) {
length += read_len;
}
}
buffer[length] = '\0';
read_len = length;
if ( rc != BZ_STREAM_END ) {
crm_err("Couldnt read compressed xml from file");
free(buffer);
buffer = NULL;
}
BZ2_bzReadClose (&rc, bz_file);
fclose(input);
#else
crm_err("Cannot read compressed files:"
" bzlib was not available at compile time");
#endif
return buffer;
}
static void strip_text_nodes(xmlNode *xml)
{
xmlNode *iter = NULL;
for(iter = xml->children; iter; iter = iter->next) {
switch(iter->type) {
case XML_TEXT_NODE:
/* Remove it */
xmlUnlinkNode(iter);
xmlFreeNode(iter);
/* Start again since xml->children will have changed */
strip_text_nodes(xml);
return;
case XML_ELEMENT_NODE:
/* Search it */
strip_text_nodes(iter);
break;
default:
/* Leave it */
break;
}
}
}
xmlNode *
filename2xml(const char *filename)
{
xmlNode *xml = NULL;
xmlDocPtr output = NULL;
const char *match = NULL;
xmlParserCtxtPtr ctxt = NULL;
xmlErrorPtr last_error = NULL;
static int xml_options = XML_PARSE_NOBLANKS|XML_PARSE_RECOVER;
/* create a parser context */
ctxt = xmlNewParserCtxt();
CRM_CHECK(ctxt != NULL, return NULL);
/* xmlCtxtUseOptions(ctxt, XML_PARSE_NOBLANKS|XML_PARSE_RECOVER); */
xmlCtxtResetLastError(ctxt);
xmlSetGenericErrorFunc(ctxt, crm_xml_err);
/* initGenericErrorDefaultFunc(crm_xml_err); */
if(filename) {
match = strstr(filename, ".bz2");
}
if(filename == NULL) {
/* STDIN_FILENO == fileno(stdin) */
output = xmlCtxtReadFd(ctxt, STDIN_FILENO, "unknown.xml", NULL, xml_options);
} else if(match == NULL || match[4] != 0) {
output = xmlCtxtReadFile(ctxt, filename, NULL, xml_options);
} else {
char *input = decompress_file(filename);
output = xmlCtxtReadDoc(ctxt, (const xmlChar*)input, NULL, NULL, xml_options);
free(input);
}
if(output && (xml = xmlDocGetRootElement(output))) {
strip_text_nodes(xml);
}
last_error = xmlCtxtGetLastError(ctxt);
if(last_error && last_error->code != XML_ERR_OK) {
/* crm_abort(__FILE__,__PRETTY_FUNCTION__,__LINE__, "last_error->code != XML_ERR_OK", TRUE, TRUE); */
/*
* http://xmlsoft.org/html/libxml-xmlerror.html#xmlErrorLevel
* http://xmlsoft.org/html/libxml-xmlerror.html#xmlParserErrors
*/
crm_err("Parsing failed (domain=%d, level=%d, code=%d): %s",
last_error->domain, last_error->level,
last_error->code, last_error->message);
if(last_error && last_error->code != XML_ERR_OK) {
crm_err("Couldn't%s parse %s", xml?" fully":"", filename);
if(xml != NULL) {
crm_log_xml_err(xml, "Partial");
}
}
}
xmlFreeParserCtxt(ctxt);
return xml;
}
int
write_xml_file(xmlNode *xml_node, const char *filename, gboolean compress)
{
int res = 0;
time_t now;
char *buffer = NULL;
char *now_str = NULL;
unsigned int out = 0;
FILE *file_output_strm = NULL;
static mode_t cib_mode = S_IRUSR|S_IWUSR;
CRM_CHECK(filename != NULL, return -1);
crm_trace("Writing XML out to %s", filename);
crm_validate_data(xml_node);
if (xml_node == NULL) {
crm_err("Cannot write NULL to %s", filename);
return -1;
}
file_output_strm = fopen(filename, "w");
if(file_output_strm == NULL) {
crm_perror(LOG_ERR,"Cannot open %s for writing", filename);
return -1;
}
/* establish the correct permissions */
fchmod(fileno(file_output_strm), cib_mode);
crm_log_xml_trace(xml_node, "Writing out");
now = time(NULL);
now_str = ctime(&now);
now_str[24] = EOS; /* replace the newline */
crm_xml_add(xml_node, XML_CIB_ATTR_WRITTEN, now_str);
crm_validate_data(xml_node);
buffer = dump_xml_formatted(xml_node);
CRM_CHECK(buffer != NULL && strlen(buffer) > 0,
crm_log_xml_warn(xml_node, "dump:failed");
goto bail);
if(compress) {
#if HAVE_BZLIB_H
int rc = BZ_OK;
unsigned int in = 0;
BZFILE *bz_file = NULL;
bz_file = BZ2_bzWriteOpen(&rc, file_output_strm, 5, 0, 30);
if(rc != BZ_OK) {
crm_err("bzWriteOpen failed: %d", rc);
} else {
BZ2_bzWrite(&rc,bz_file,buffer,strlen(buffer));
if(rc != BZ_OK) {
crm_err("bzWrite() failed: %d", rc);
}
}
if(rc == BZ_OK) {
BZ2_bzWriteClose(&rc, bz_file, 0, &in, &out);
if(rc != BZ_OK) {
crm_err("bzWriteClose() failed: %d",rc);
out = -1;
} else {
crm_trace("%s: In: %d, out: %d", filename, in, out);
}
}
#else
crm_err("Cannot write compressed files:"
" bzlib was not available at compile time");
#endif
}
if(out <= 0) {
res = fprintf(file_output_strm, "%s", buffer);
if(res < 0) {
crm_perror(LOG_ERR,"Cannot write output to %s", filename);
goto bail;
}
}
bail:
if(fflush(file_output_strm) != 0) {
crm_perror(LOG_ERR,"fflush for %s failed:", filename);
res = -1;
}
if(fsync(fileno(file_output_strm)) < 0) {
crm_perror(LOG_ERR,"fsync for %s failed:", filename);
res = -1;
}
fclose(file_output_strm);
crm_trace("Saved %d bytes to the Cib as XML", res);
free(buffer);
return res;
}
xmlNode *
get_message_xml(xmlNode *msg, const char *field)
{
xmlNode *tmp = first_named_child(msg, field);
return __xml_first_child(tmp);
}
gboolean
add_message_xml(xmlNode *msg, const char *field, xmlNode *xml)
{
xmlNode *holder = create_xml_node(msg, field);
add_node_copy(holder, xml);
return TRUE;
}
static char *
dump_xml(xmlNode *an_xml_node, gboolean formatted, gboolean for_digest)
{
int len = 0;
char *buffer = NULL;
xmlBuffer *xml_buffer = NULL;
xmlDoc *doc = getDocPtr(an_xml_node);
/* doc will only be NULL if an_xml_node is */
CRM_CHECK(doc != NULL, return NULL);
xml_buffer = xmlBufferCreate();
CRM_ASSERT(xml_buffer != NULL);
len = xmlNodeDump(xml_buffer, doc, an_xml_node, 0, formatted);
if(len > 0) {
/* The copying here isn't ideal, but it doesn't even register
* in the perf numbers
*/
if(for_digest) {
/* for compatability with the old result which is used for digests */
len += 3;
buffer = calloc(1, len);
snprintf(buffer, len, " %s\n", (char *)xml_buffer->content);
} else {
buffer = crm_strdup((char *)xml_buffer->content);
}
} else {
crm_err("Conversion failed");
}
xmlBufferFree(xml_buffer);
return buffer;
}
char *
dump_xml_formatted(xmlNode *an_xml_node)
{
return dump_xml(an_xml_node, TRUE, FALSE);
}
char *
dump_xml_unformatted(xmlNode *an_xml_node)
{
return dump_xml(an_xml_node, FALSE, FALSE);
}
#define update_buffer() do { \
if(printed < 0) { \
crm_perror(LOG_ERR,"snprintf failed"); \
goto print; \
} else if(printed >= (buffer_len - offset)) { \
crm_err("Output truncated: available=%d, needed=%d", buffer_len - offset, printed); \
offset += printed; \
goto print; \
} else if(offset >= buffer_len) { \
crm_err("Buffer exceeded"); \
offset += printed; \
goto print; \
} else { \
offset += printed; \
} \
} while(0)
int
print_spaces(char *buffer, int depth, int max)
{
int lpc = 0;
int spaces = 2*depth;
max--;
/* <= so that we always print 1 space - prevents problems with syslog */
for(lpc = 0; lpc <= spaces && lpc < max; lpc++) {
if(sprintf(buffer+lpc, "%c", ' ') < 1) {
return -1;
}
}
return lpc;
}
int
log_data_element(
int log_level, const char *file, const char *function, int line,
const char *prefix, xmlNode *data, int depth, int options)
{
xmlNode *a_child = NULL;
int child_result = 0;
int offset = 0;
int printed = 0;
char *buffer = NULL;
char *prefix_m = NULL;
int buffer_len = 1000;
const char *name = NULL;
const char *hidden = NULL;
gboolean formatted = (options & 0x1);
gboolean diff_plus = (options & 0x2);
gboolean diff_minus = (options & 0x4);
/* Since we use the same file and line, to avoid confusing libqb, we need to use the same format strings */
if(data == NULL) {
do_crm_log_alias(log_level, file, function, line, "%s%s", prefix, ": No data to dump as XML");
return 0;
}
if(prefix == NULL) {
prefix = "";
}
name = crm_element_name(data);
CRM_ASSERT(name != NULL);
/* crm_trace("Dumping %s", name); */
buffer = calloc(1, buffer_len);
if(formatted) {
offset = print_spaces(buffer, depth, buffer_len - offset);
if(diff_plus && crm_element_value(data, XML_DIFF_MARKER)) {
prefix_m = strdup(prefix);
prefix_m[1] = '+';
prefix = prefix_m;
} else if(diff_minus && crm_element_value(data, XML_DIFF_MARKER)) {
prefix_m = strdup(prefix);
prefix_m[1] = '-';
prefix = prefix_m;
}
}
printed = snprintf(buffer + offset, buffer_len - offset, "<%s", name);
update_buffer();
hidden = crm_element_value(data, "hidden");
xml_prop_iter(
data, prop_name, prop_value,
if(prop_name == NULL
|| safe_str_eq(F_XML_TAGNAME, prop_name)) {
continue;
} else if((diff_plus || diff_minus) && safe_str_eq(XML_DIFF_MARKER, prop_name)) {
continue;
} else if(hidden != NULL
&& prop_name[0] != 0
&& strstr(hidden, prop_name) != NULL) {
prop_value = "*****";
}
/* crm_trace("Dumping <%s %s=\"%s\"...", */
/* name, prop_name, prop_value); */
printed = snprintf(buffer + offset, buffer_len - offset,
" %s=\"%s\"", prop_name, prop_value);
update_buffer();
);
printed = snprintf(buffer + offset, buffer_len - offset,
" %s>", xml_has_children(data)?"":"/");
update_buffer();
print:
do_crm_log_alias(log_level, file, function, line, "%s%s", prefix, buffer);
if(xml_has_children(data) == FALSE) {
free(prefix_m);
free(buffer);
return 0;
}
for(a_child = __xml_first_child(data); a_child != NULL; a_child = __xml_next(a_child)) {
child_result = log_data_element(
log_level, file, function, line, prefix, a_child, depth+1, options);
}
if(formatted) {
offset = print_spaces(buffer, depth, buffer_len);
}
printed = snprintf(buffer + offset, buffer_len - offset, "</%s>", name);
update_buffer();
do_crm_log_alias(log_level, file, function, line, "%s%s", prefix, buffer);
free(prefix_m);
free(buffer);
return 1;
}
gboolean
xml_has_children(const xmlNode *xml_root)
{
if(xml_root != NULL && xml_root->children != NULL) {
return TRUE;
}
return FALSE;
}
void
xml_validate(const xmlNode *xml_root)
{
CRM_ASSERT(xml_root != NULL);
}
int
crm_element_value_int(xmlNode *data, const char *name, int *dest)
{
const char *value = crm_element_value(data, name);
CRM_CHECK(dest != NULL, return -1);
if(value) {
*dest = crm_int_helper(value, NULL);
return 0;
}
return -1;
}
int
crm_element_value_const_int(const xmlNode *data, const char *name, int *dest)
{
return crm_element_value_int((xmlNode*)data, name, dest);
}
const char *
crm_element_value_const(const xmlNode *data, const char *name)
{
return crm_element_value((xmlNode*)data, name);
}
char *
crm_element_value_copy(xmlNode *data, const char *name)
{
char *value_copy = NULL;
const char *value = crm_element_value(data, name);
if(value != NULL) {
value_copy = crm_strdup(value);
}
return value_copy;
}
void
xml_remove_prop(xmlNode *obj, const char *name)
{
xmlUnsetProp(obj, (const xmlChar*)name);
}
void
log_xml_diff(unsigned int log_level, xmlNode *diff, const char *function)
{
xmlNode *child = NULL;
xmlNode *added = find_xml_node(diff, "diff-added", FALSE);
xmlNode *removed = find_xml_node(diff, "diff-removed", FALSE);
gboolean is_first = TRUE;
static struct qb_log_callsite *diff_cs = NULL;
if(diff_cs == NULL) {
diff_cs = qb_log_callsite_get(function, __FILE__, "xml-diff", log_level, __LINE__, 0);
}
if (crm_is_callsite_active(diff_cs, log_level) == FALSE) {
return;
}
for(child = __xml_first_child(removed); child != NULL; child = __xml_next(child)) {
log_data_element(log_level, __FILE__, function, __LINE__, "- ", child, 0, 5);
if(is_first) {
is_first = FALSE;
} else {
do_crm_log(log_level, " --- ");
}
}
is_first = TRUE;
for(child = __xml_first_child(added); child != NULL; child = __xml_next(child)) {
log_data_element(log_level, __FILE__, function, __LINE__, "+ ", child, 0, 3);
if(is_first) {
is_first = FALSE;
} else {
do_crm_log(log_level, " +++ ");
}
}
}
void
purge_diff_markers(xmlNode *a_node)
{
xmlNode *child = NULL;
CRM_CHECK(a_node != NULL, return);
xml_remove_prop(a_node, XML_DIFF_MARKER);
for(child = __xml_first_child(a_node); child != NULL; child = __xml_next(child)) {
purge_diff_markers(child);
}
}
gboolean
apply_xml_diff(xmlNode *old, xmlNode *diff, xmlNode **new)
{
gboolean result = TRUE;
int root_nodes_seen = 0;
const char *digest = crm_element_value(diff, XML_ATTR_DIGEST);
const char *version = crm_element_value(diff, XML_ATTR_CRM_VERSION);
xmlNode *child_diff = NULL;
xmlNode *added = find_xml_node(diff, "diff-added", FALSE);
xmlNode *removed = find_xml_node(diff, "diff-removed", FALSE);
CRM_CHECK(new != NULL, return FALSE);
crm_trace("Substraction Phase");
for(child_diff = __xml_first_child(removed); child_diff != NULL; child_diff = __xml_next(child_diff)) {
CRM_CHECK(root_nodes_seen == 0, result = FALSE);
if(root_nodes_seen == 0) {
*new = subtract_xml_object(NULL, old, child_diff, FALSE, NULL);
}
root_nodes_seen++;
}
if(root_nodes_seen == 0) {
*new = copy_xml(old);
} else if(root_nodes_seen > 1) {
crm_err("(-) Diffs cannot contain more than one change set..."
" saw %d", root_nodes_seen);
result = FALSE;
}
root_nodes_seen = 0;
crm_trace("Addition Phase");
if(result) {
xmlNode *child_diff = NULL;
for(child_diff = __xml_first_child(added); child_diff != NULL; child_diff = __xml_next(child_diff)) {
CRM_CHECK(root_nodes_seen == 0, result = FALSE);
if(root_nodes_seen == 0) {
add_xml_object(NULL, *new, child_diff, TRUE);
}
root_nodes_seen++;
}
}
if(root_nodes_seen > 1) {
crm_err("(+) Diffs cannot contain more than one change set..."
" saw %d", root_nodes_seen);
result = FALSE;
} else if(result && digest) {
char *new_digest = NULL;
purge_diff_markers(*new); /* Purge now so the diff is ok */
new_digest = calculate_xml_versioned_digest(*new, FALSE, TRUE, version);
if(safe_str_neq(new_digest, digest)) {
crm_info("Digest mis-match: expected %s, calculated %s",
digest, new_digest);
crm_log_xml_trace(old, "diff:original");
crm_log_xml_trace(diff, "diff:input");
result = FALSE;
} else {
crm_trace("Digest matched: expected %s, calculated %s",
digest, new_digest);
}
free(new_digest);
#if XML_PARANOIA_CHECKS
} else if(result) {
int lpc = 0;
xmlNode *intermediate = NULL;
xmlNode *diff_of_diff = NULL;
xmlNode *calc_added = NULL;
xmlNode *calc_removed = NULL;
const char *value = NULL;
const char *name = NULL;
const char *version_attrs[] = {
XML_ATTR_NUMUPDATES,
XML_ATTR_GENERATION,
XML_ATTR_GENERATION_ADMIN
};
crm_trace("Verification Phase");
intermediate = diff_xml_object(old, *new, FALSE);
calc_added = find_xml_node(intermediate, "diff-added", FALSE);
calc_removed = find_xml_node(intermediate, "diff-removed", FALSE);
/* add any version details to the diff so they match */
for(lpc = 0; lpc < DIMOF(version_attrs); lpc++) {
name = version_attrs[lpc];
value = crm_element_value(added, name);
crm_xml_add(calc_added, name, value);
value = crm_element_value(removed, name);
crm_xml_add(calc_removed, name, value);
}
diff_of_diff = diff_xml_object(intermediate, diff, TRUE);
if(diff_of_diff != NULL) {
crm_info("Diff application failed!");
crm_log_xml_debug(old, "diff:original");
crm_log_xml_debug(diff, "diff:input");
result = FALSE;
} else {
purge_diff_markers(*new);
}
free_xml(diff_of_diff);
free_xml(intermediate);
diff_of_diff = NULL;
intermediate = NULL;
#endif
}
return result;
}
xmlNode *
diff_xml_object(xmlNode *old, xmlNode *new, gboolean suppress)
{
xmlNode *tmp1 = NULL;
xmlNode *diff = create_xml_node(NULL, "diff");
xmlNode *removed = create_xml_node(diff, "diff-removed");
xmlNode *added = create_xml_node(diff, "diff-added");
crm_xml_add(diff, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
tmp1 = subtract_xml_object(removed, old, new, FALSE, "removed:top");
if(suppress && tmp1 != NULL && can_prune_leaf(tmp1)) {
free_xml_from_parent(removed, tmp1);
}
tmp1 = subtract_xml_object(added, new, old, TRUE, "added:top");
if(suppress && tmp1 != NULL && can_prune_leaf(tmp1)) {
free_xml_from_parent(added, tmp1);
}
if(added->children == NULL && removed->children == NULL) {
free_xml(diff);
diff = NULL;
}
return diff;
}
gboolean
can_prune_leaf(xmlNode *xml_node)
{
xmlNode *child = NULL;
gboolean can_prune = TRUE;
/* return FALSE; */
xml_prop_name_iter(xml_node, prop_name,
if(safe_str_eq(prop_name, XML_ATTR_ID)) {
continue;
}
can_prune = FALSE;
);
for(child = __xml_first_child(xml_node); child != NULL; child = __xml_next(child)) {
if(can_prune_leaf(child)) {
free_xml(child);
} else {
can_prune = FALSE;
}
}
return can_prune;
}
void
diff_filter_context(int context, int upper_bound, int lower_bound,
xmlNode *xml_node, xmlNode *parent)
{
xmlNode *us = NULL;
xmlNode *child = NULL;
xmlNode *new_parent = parent;
const char *name = crm_element_name(xml_node);
CRM_CHECK(xml_node != NULL && name != NULL, return);
us = create_xml_node(parent, name);
xml_prop_iter(xml_node, prop_name, prop_value,
lower_bound = context;
crm_xml_add(us, prop_name, prop_value);
);
if(lower_bound >= 0 || upper_bound >= 0) {
crm_xml_add(us, XML_ATTR_ID, ID(xml_node));
new_parent = us;
} else {
upper_bound = in_upper_context(0, context, xml_node);
if(upper_bound >= 0) {
crm_xml_add(us, XML_ATTR_ID, ID(xml_node));
new_parent = us;
} else {
free_xml(us);
us = NULL;
}
}
for(child = __xml_first_child(us); child != NULL; child = __xml_next(child)) {
diff_filter_context(context, upper_bound-1, lower_bound-1,
child, new_parent);
}
}
int
in_upper_context(int depth, int context, xmlNode *xml_node)
{
gboolean has_attributes = FALSE;
if(context == 0) {
return 0;
}
xml_prop_name_iter(xml_node, prop_name, has_attributes = TRUE; break);
if(has_attributes) {
return depth;
} else if(depth < context) {
xmlNode *child = NULL;
for(child = __xml_first_child(xml_node); child != NULL; child = __xml_next(child)) {
if(in_upper_context(depth+1, context, child)) {
return depth;
}
}
}
return 0;
}
xmlNode *
subtract_xml_object(xmlNode *parent, xmlNode *left, xmlNode *right, gboolean full, const char *marker)
{
gboolean skip = FALSE;
gboolean differences = FALSE;
xmlNode *diff = NULL;
xmlNode *child_diff = NULL;
xmlNode *right_child = NULL;
xmlNode *left_child = NULL;
const char *id = NULL;
const char *name = NULL;
const char *value = NULL;
const char *right_val = NULL;
int lpc = 0;
static int filter_len = DIMOF(filter);
if(left == NULL) {
return NULL;
}
id = ID(left);
if(right == NULL) {
xmlNode *deleted = NULL;
crm_trace("Processing <%s id=%s> (complete copy)",
crm_element_name(left), id);
deleted = add_node_copy(parent, left);
crm_xml_add(deleted, XML_DIFF_MARKER, marker);
return deleted;
}
name = crm_element_name(left);
CRM_CHECK(name != NULL, return NULL);
/* Avoiding creating the full heirarchy would save even more work here */
diff = create_xml_node(parent, name);
/* Reset filter */
for(lpc = 0; lpc < filter_len; lpc++){
filter[lpc].found = FALSE;
}
/* changes to child objects */
for(left_child = __xml_first_child(left); left_child != NULL; left_child = __xml_next(left_child)) {
right_child = find_entity(
right, crm_element_name(left_child), ID(left_child));
child_diff = subtract_xml_object(diff, left_child, right_child, full, marker);
if(child_diff != NULL) {
differences = TRUE;
}
}
if(differences == FALSE) {
/* check for XML_DIFF_MARKER in a child */
for(right_child = __xml_first_child(right); right_child != NULL; right_child = __xml_next(right_child)) {
value = crm_element_value(right_child, XML_DIFF_MARKER);
if(value != NULL && safe_str_eq(value, "removed:top")) {
crm_trace("Found the root of the deletion: %s", name);
xml_prop_iter(left, name, value, xmlSetProp(diff, (const xmlChar*)name, (const xmlChar*)value));
differences = TRUE;
goto done;
}
}
} else if(full) {
xml_prop_iter(left, name, value, xmlSetProp(diff, (const xmlChar*)name, (const xmlChar*)value));
/* We already have everything we need... */
goto done;
} else if(id) {
xmlSetProp(diff, (const xmlChar*)XML_ATTR_ID, (const xmlChar*)id);
}
/* changes to name/value pairs */
xml_prop_name_iter(
left, prop_name,
if(crm_str_eq(prop_name, XML_ATTR_ID, TRUE)) {
continue;
}
skip = FALSE;
for(lpc = 0; skip == FALSE && lpc < filter_len; lpc++){
if(filter[lpc].found == FALSE && crm_str_eq(prop_name, filter[lpc].string, TRUE)) {
filter[lpc].found = TRUE;
skip = TRUE;
break;
}
}
if(skip) { continue; }
right_val = crm_element_value(right, prop_name);
if(right_val == NULL) {
/* new */
differences = TRUE;
if(full) {
xml_prop_iter(left, name, value, xmlSetProp(diff, (const xmlChar*)name, (const xmlChar*)value));
break;
} else {
const char *left_value = crm_element_value(left, prop_name);
xmlSetProp(diff, (const xmlChar*)prop_name, (const xmlChar*)value);
crm_xml_add(diff, prop_name, left_value);
}
} else {
/* Only now do we need the left value */
const char *left_value = crm_element_value(left, prop_name);
if(strcmp(left_value, right_val) == 0) {
/* unchanged */
} else {
/* changed */
differences = TRUE;
if(full) {
xml_prop_iter(left, name, value, xmlSetProp(diff, (const xmlChar*)name, (const xmlChar*)value));
break;
} else {
crm_xml_add(diff, prop_name, left_value);
}
}
}
);
if(differences == FALSE) {
free_xml_from_parent(parent, diff);
crm_trace("\tNo changes to <%s id=%s>", crm_str(name), id);
return NULL;
} else if(full == FALSE && id) {
crm_xml_add(diff, XML_ATTR_ID, id);
}
done:
return diff;
}
int
add_xml_object(xmlNode *parent, xmlNode *target, xmlNode *update, gboolean as_diff)
{
xmlNode *a_child = NULL;
const char *object_id = NULL;
const char *object_name = NULL;
#if XML_PARSE_DEBUG
crm_log_xml_trace("update:", update);
crm_log_xml_trace("target:", target);
#endif
CRM_CHECK(update != NULL, return 0);
object_name = crm_element_name(update);
object_id = ID(update);
CRM_CHECK(object_name != NULL, return 0);
if(target == NULL && object_id == NULL) {
/* placeholder object */
target = find_xml_node(parent, object_name, FALSE);
} else if(target == NULL) {
target = find_entity(parent, object_name, object_id);
}
if(target == NULL) {
target = create_xml_node(parent, object_name);
CRM_CHECK(target != NULL, return 0);
#if XML_PARSER_DEBUG
crm_trace("Added <%s%s%s/>", crm_str(object_name),
object_id?" id=":"", object_id?object_id:"");
} else {
crm_trace("Found node <%s%s%s/> to update",
crm_str(object_name),
object_id?" id=":"", object_id?object_id:"");
#endif
}
if(as_diff == FALSE) {
/* So that expand_plus_plus() gets called */
copy_in_properties(target, update);
} else {
/* No need for expand_plus_plus(), just raw speed */
xml_prop_iter(update, p_name, p_value,
/* Remove it first so the ordering of the update is preserved */
xmlUnsetProp(target, (const xmlChar*)p_name);
xmlSetProp(target, (const xmlChar*)p_name, (const xmlChar*)p_value));
}
for(a_child = __xml_first_child(update); a_child != NULL; a_child = __xml_next(a_child)) {
#if XML_PARSER_DEBUG
crm_trace("Updating child <%s id=%s>",
crm_element_name(a_child), ID(a_child));
#endif
add_xml_object(target, NULL, a_child, as_diff);
}
#if XML_PARSER_DEBUG
crm_trace("Finished with <%s id=%s>",
crm_str(object_name), crm_str(object_id));
#endif
return 0;
}
gboolean
update_xml_child(xmlNode *child, xmlNode *to_update)
{
gboolean can_update = TRUE;
xmlNode *child_of_child = NULL;
CRM_CHECK(child != NULL, return FALSE);
CRM_CHECK(to_update != NULL, return FALSE);
if(safe_str_neq(crm_element_name(to_update), crm_element_name(child))) {
can_update = FALSE;
} else if(safe_str_neq(ID(to_update), ID(child))) {
can_update = FALSE;
} else if(can_update) {
#if XML_PARSER_DEBUG
crm_log_xml_trace(child, "Update match found...");
#endif
add_xml_object(NULL, child, to_update, FALSE);
}
for(child_of_child = __xml_first_child(child); child_of_child != NULL; child_of_child = __xml_next(child_of_child)) {
/* only update the first one */
if(can_update) {
break;
}
can_update = update_xml_child(child_of_child, to_update);
}
return can_update;
}
int
find_xml_children(xmlNode **children, xmlNode *root,
const char *tag, const char *field, const char *value,
gboolean search_matches)
{
int match_found = 0;
CRM_CHECK(root != NULL, return FALSE);
CRM_CHECK(children != NULL, return FALSE);
if(tag != NULL && safe_str_neq(tag, crm_element_name(root))) {
} else if(value != NULL
&& safe_str_neq(value, crm_element_value(root, field))) {
} else {
if(*children == NULL) {
*children = create_xml_node(NULL, __FUNCTION__);
}
add_node_copy(*children, root);
match_found = 1;
}
if(search_matches || match_found == 0) {
xmlNode *child = NULL;
for(child = __xml_first_child(root); child != NULL; child = __xml_next(child)) {
match_found += find_xml_children(
children, child, tag, field, value,
search_matches);
}
}
return match_found;
}
gboolean
replace_xml_child(xmlNode *parent, xmlNode *child, xmlNode *update, gboolean delete_only)
{
gboolean can_delete = FALSE;
xmlNode *child_of_child = NULL;
const char *up_id = NULL;
const char *child_id = NULL;
const char *right_val = NULL;
CRM_CHECK(child != NULL, return FALSE);
CRM_CHECK(update != NULL, return FALSE);
up_id = ID(update);
child_id = ID(child);
if(up_id == NULL || safe_str_eq(child_id, up_id)) {
can_delete = TRUE;
}
if(safe_str_neq(crm_element_name(update), crm_element_name(child))) {
can_delete = FALSE;
}
if(can_delete && delete_only) {
xml_prop_iter(update, prop_name, left_value,
right_val = crm_element_value(child, prop_name);
if(safe_str_neq(left_value, right_val)) {
can_delete = FALSE;
}
);
}
if(can_delete && parent != NULL) {
crm_log_xml_trace(child, "Delete match found...");
if(delete_only || update == NULL) {
free_xml_from_parent(NULL, child);
} else {
xmlNode *tmp = copy_xml(update);
xmlDoc *doc = tmp->doc;
xmlNode *old = xmlReplaceNode(child, tmp);
free_xml_from_parent(NULL, old);
xmlDocSetRootElement(doc, NULL);
xmlFreeDoc(doc);
}
child = NULL;
return TRUE;
} else if(can_delete) {
crm_log_xml_debug(child, "Cannot delete the search root");
can_delete = FALSE;
}
child_of_child = __xml_first_child(child);
while(child_of_child) {
xmlNode *next = __xml_next(child_of_child);
can_delete = replace_xml_child(child, child_of_child, update, delete_only);
/* only delete the first one */
if(can_delete) {
child_of_child = NULL;
} else {
child_of_child = next;
}
}
return can_delete;
}
void
hash2nvpair(gpointer key, gpointer value, gpointer user_data)
{
const char *name = key;
const char *s_value = value;
xmlNode *xml_node = user_data;
xmlNode *xml_child = create_xml_node(xml_node, XML_CIB_TAG_NVPAIR);
crm_xml_add(xml_child, XML_ATTR_ID, name);
crm_xml_add(xml_child, XML_NVPAIR_ATTR_NAME, name);
crm_xml_add(xml_child, XML_NVPAIR_ATTR_VALUE, s_value);
crm_trace("dumped: name=%s value=%s", name, s_value);
}
void
hash2smartfield(gpointer key, gpointer value, gpointer user_data)
{
const char *name = key;
const char *s_value = value;
xmlNode *xml_node = user_data;
if(isdigit(name[0])) {
xmlNode *tmp = create_xml_node(xml_node, XML_TAG_PARAM);
crm_xml_add(tmp, XML_NVPAIR_ATTR_NAME, name);
crm_xml_add(tmp, XML_NVPAIR_ATTR_VALUE, s_value);
} else if(crm_element_value(xml_node, name) == NULL) {
crm_xml_add(xml_node, name, s_value);
crm_trace("dumped: %s=%s", name, s_value);
} else {
crm_trace("duplicate: %s=%s", name, s_value);
}
}
void
hash2field(gpointer key, gpointer value, gpointer user_data)
{
const char *name = key;
const char *s_value = value;
xmlNode *xml_node = user_data;
if(crm_element_value(xml_node, name) == NULL) {
crm_xml_add(xml_node, name, s_value);
crm_trace("dumped: %s=%s", name, s_value);
} else {
crm_trace("duplicate: %s=%s", name, s_value);
}
}
void
hash2metafield(gpointer key, gpointer value, gpointer user_data)
{
char *crm_name = NULL;
if(key == NULL || value == NULL) {
return;
} else if(((char*)key)[0] == '#') {
return;
} else if(strstr(key, ":")) {
return;
}
crm_name = crm_meta_name(key);
hash2field(crm_name, value, user_data);
free(crm_name);
}
GHashTable *
xml2list(xmlNode *parent)
{
xmlNode *child = NULL;
xmlNode *nvpair_list = NULL;
GHashTable *nvpair_hash = g_hash_table_new_full(
crm_str_hash, g_str_equal,
g_hash_destroy_str, g_hash_destroy_str);
CRM_CHECK(parent != NULL, return nvpair_hash);
nvpair_list = find_xml_node(parent, XML_TAG_ATTRS, FALSE);
if(nvpair_list == NULL) {
crm_trace("No attributes in %s",
crm_element_name(parent));
crm_log_xml_trace(
parent,"No attributes for resource op");
}
crm_log_xml_trace(nvpair_list, "Unpacking");
xml_prop_iter(
nvpair_list, key, value,
crm_trace("Added %s=%s", key, value);
g_hash_table_insert(
nvpair_hash, crm_strdup(key), crm_strdup(value));
);
for(child = __xml_first_child(nvpair_list); child != NULL; child = __xml_next(child)) {
if(crm_str_eq((const char *)child->name, XML_TAG_PARAM, TRUE)) {
const char *key = crm_element_value(child, XML_NVPAIR_ATTR_NAME);
const char *value = crm_element_value(child, XML_NVPAIR_ATTR_VALUE);
crm_trace("Added %s=%s", key, value);
if(key != NULL && value != NULL) {
g_hash_table_insert(nvpair_hash, crm_strdup(key), crm_strdup(value));
}
}
}
return nvpair_hash;
}
typedef struct name_value_s
{
const char *name;
const void *value;
} name_value_t;
static gint
sort_pairs(gconstpointer a, gconstpointer b)
{
int rc = 0;
const name_value_t *pair_a = a;
const name_value_t *pair_b = b;
CRM_ASSERT(a != NULL);
CRM_ASSERT(pair_a->name != NULL);
CRM_ASSERT(b != NULL);
CRM_ASSERT(pair_b->name != NULL);
rc = strcmp(pair_a->name, pair_b->name);
if(rc < 0) {
return -1;
} else if(rc > 0) {
return 1;
}
return 0;
}
static void
dump_pair(gpointer data, gpointer user_data)
{
name_value_t *pair = data;
xmlNode *parent = user_data;
crm_xml_add(parent, pair->name, pair->value);
}
xmlNode *
sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
{
xmlNode *child = NULL;
GListPtr sorted = NULL;
GListPtr unsorted = NULL;
name_value_t *pair = NULL;
xmlNode *result = NULL;
const char *name = NULL;
CRM_CHECK(input != NULL, return NULL);
name = crm_element_name(input);
CRM_CHECK(name != NULL, return NULL);
result = create_xml_node(parent, name);
xml_prop_iter(input, p_name, p_value,
pair = calloc(1, sizeof(name_value_t));
pair->name = p_name;
pair->value = p_value;
unsorted = g_list_prepend(unsorted, pair);
pair = NULL;
);
sorted = g_list_sort(unsorted, sort_pairs);
g_list_foreach(sorted, dump_pair, result);
- slist_basic_destroy(sorted);
+ g_list_free_full(sorted, free);
for(child = __xml_first_child(input); child != NULL; child = __xml_next(child)) {
if(recursive) {
sorted_xml(child, result, recursive);
} else {
add_node_copy(result, child);
}
}
return result;
}
static void
filter_xml(xmlNode *data, filter_t *filter, int filter_len, gboolean recursive)
{
int lpc = 0;
xmlNode *child = NULL;
for(lpc = 0; lpc < filter_len; lpc++) {
xml_remove_prop(data, filter[lpc].string);
}
if(recursive == FALSE || filter_len == 0) {
return;
}
for(child = __xml_first_child(data); child != NULL; child = __xml_next(child)) {
filter_xml(child, filter, filter_len, recursive);
}
}
/* "c048eae664dba840e1d2060f00299e9d" */
static char *
calculate_xml_digest_v1(xmlNode *input, gboolean sort, gboolean do_filter)
{
char *digest = NULL;
xmlNode *copy = NULL;
char *buffer = NULL;
if(sort || do_filter) {
copy = sorted_xml(input, NULL, TRUE);
input = copy;
}
if(do_filter) {
filter_xml(input, filter, DIMOF(filter), TRUE);
}
buffer = dump_xml(input, FALSE, TRUE);
CRM_CHECK(buffer != NULL && strlen(buffer) > 0, free_xml(copy); free(buffer); return NULL);
digest = crm_md5sum(buffer);
crm_trace("Digest %s: %s\n", digest, buffer);
crm_log_xml_trace(copy, "digest:source");
free(buffer);
free_xml(copy);
return digest;
}
static char *
calculate_xml_digest_v2(xmlNode *input, gboolean do_filter)
{
char *digest = NULL;
int buffer_len = 0;
int filter_size = DIMOF(filter);
xmlDoc *doc = NULL;
xmlNode *copy = NULL;
xmlBuffer *xml_buffer = NULL;
static struct qb_log_callsite *digest_cs = NULL;
if(do_filter && BEST_EFFORT_STATUS) {
/* Exclude the status calculation from the digest
*
* This doesn't mean it wont be sync'd, we just wont be paranoid
* about it being an _exact_ copy
*
* We don't need it to be exact, since we throw it away and regenerate
* from our peers whenever a new DC is elected anyway
*
* Importantly, this reduces the amount of XML to copy+export as
* well as the amount of data for MD5 needs to operate on
*/
xmlNode *child = NULL;
copy = create_xml_node(NULL, XML_TAG_CIB);
xml_prop_iter(input, p_name, p_value,
xmlSetProp(copy, (const xmlChar*)p_name, (const xmlChar*)p_value));
xml_remove_prop(copy, XML_ATTR_ORIGIN);
xml_remove_prop(copy, XML_CIB_ATTR_WRITTEN);
/* We just did all the filtering */
for(child = __xml_first_child(input); child != NULL; child = __xml_next(child)) {
if(safe_str_neq(crm_element_name(child), XML_CIB_TAG_STATUS)) {
add_node_copy(copy, child);
}
}
} else if(do_filter) {
copy = copy_xml(input);
filter_xml(copy, filter, filter_size, TRUE);
input = copy;
}
doc = getDocPtr(input);
xml_buffer = xmlBufferCreate();
CRM_ASSERT(xml_buffer != NULL);
CRM_CHECK(doc != NULL, return NULL); /* doc will only be NULL if an_xml_node is */
buffer_len = xmlNodeDump(xml_buffer, doc, input, 0, FALSE);
CRM_CHECK(xml_buffer->content != NULL && buffer_len > 0, goto done);
digest = crm_md5sum((char *)xml_buffer->content);
crm_trace("Digest %s\n", digest);
if(digest_cs == NULL) {
qb_log_callsite_get(__func__, __FILE__, "xml-blog", LOG_TRACE, __LINE__, 0);
}
if (digest_cs && digest_cs->targets) {
FILE *st = NULL;
char *trace_file = crm_concat("/tmp/cib-digest", digest, '-');
crm_trace("Saving %s.%s.%s to %s",
crm_element_value(input, XML_ATTR_GENERATION_ADMIN),
crm_element_value(input, XML_ATTR_GENERATION),
crm_element_value(input, XML_ATTR_NUMUPDATES),
trace_file);
st = fopen(trace_file, "w");
if(st) {
fprintf(st, "%s", xml_buffer->content);
/* fflush(st); */
/* fsync(fileno(st)); */
fclose(st);
}
free(trace_file);
}
done:
xmlBufferFree(xml_buffer);
free_xml(copy);
return digest;
}
char *
calculate_on_disk_digest(xmlNode *input)
{
/* Always use the v1 format for on-disk digests
* a) its a compatability nightmare
* b) we only use this once at startup, all other
* invocations are in a separate child process
*/
return calculate_xml_digest_v1(input, FALSE, FALSE);
}
char *
calculate_operation_digest(xmlNode *input, const char *version)
{
/* We still need the sorting for parameter digests */
return calculate_xml_digest_v1(input, TRUE, FALSE);
}
char *
calculate_xml_digest(xmlNode *input, gboolean sort, gboolean do_filter)
{
return calculate_xml_digest_v1(input, sort, do_filter);
}
char *
calculate_xml_versioned_digest(xmlNode *input, gboolean sort, gboolean do_filter, const char *version)
{
/*
* The sorting associated with v1 digest creation accounted for 23% of
* the CIB's CPU usage on the server. v2 drops this.
*
* The filtering accounts for an additional 2.5% and we may want to
* remove it in future.
*
* v2 also uses the xmlBuffer contents directly to avoid additional copying
*/
if(version == NULL || compare_version("3.0.5", version) > 0) {
crm_trace("Using v1 digest algorithm for %s", crm_str(version));
return calculate_xml_digest_v1(input, sort, do_filter);
}
crm_trace("Using v2 digest algorithm for %s", crm_str(version));
return calculate_xml_digest_v2(input, do_filter);
}
static gboolean
validate_with_dtd(
xmlDocPtr doc, gboolean to_logs, const char *dtd_file)
{
gboolean valid = TRUE;
xmlDtdPtr dtd = NULL;
xmlValidCtxtPtr cvp = NULL;
CRM_CHECK(doc != NULL, return FALSE);
CRM_CHECK(dtd_file != NULL, return FALSE);
dtd = xmlParseDTD(NULL, (const xmlChar *)dtd_file);
CRM_CHECK(dtd != NULL, crm_err("Could not find/parse %s", dtd_file); goto cleanup);
cvp = xmlNewValidCtxt();
CRM_CHECK(cvp != NULL, goto cleanup);
if(to_logs) {
cvp->userData = (void *) LOG_ERR;
cvp->error = (xmlValidityErrorFunc) xml_log;
cvp->warning = (xmlValidityWarningFunc) xml_log;
} else {
cvp->userData = (void *) stderr;
cvp->error = (xmlValidityErrorFunc) fprintf;
cvp->warning = (xmlValidityWarningFunc) fprintf;
}
if (!xmlValidateDtd(cvp, doc, dtd)) {
valid = FALSE;
}
cleanup:
if(cvp) {
xmlFreeValidCtxt(cvp);
}
if(dtd) {
xmlFreeDtd(dtd);
}
return valid;
}
xmlNode *first_named_child(xmlNode *parent, const char *name)
{
xmlNode *match = NULL;
for(match = __xml_first_child(parent); match != NULL; match = __xml_next(match)) {
/*
* name == NULL gives first child regardless of name; this is
* semantically incorrect in this funciton, but may be necessary
* due to prior use of xml_child_iter_filter
*/
if(name == NULL || crm_str_eq((const char*)match->name, name, TRUE)) {
return match;
}
}
return NULL;
}
#if 0
static void relaxng_invalid_stderr(void * userData, xmlErrorPtr error)
{
/*
Structure xmlError
struct _xmlError {
int domain : What part of the library raised this er
int code : The error code, e.g. an xmlParserError
char * message : human-readable informative error messag
xmlErrorLevel level : how consequent is the error
char * file : the filename
int line : the line number if available
char * str1 : extra string information
char * str2 : extra string information
char * str3 : extra string information
int int1 : extra number information
int int2 : column number of the error or 0 if N/A
void * ctxt : the parser context if available
void * node : the node in the tree
}
*/
crm_err("Structured error: line=%d, level=%d %s",
error->line, error->level, error->message);
}
#endif
static gboolean
validate_with_relaxng(
xmlDocPtr doc, gboolean to_logs, const char *relaxng_file, relaxng_ctx_cache_t **cached_ctx)
{
int rc = 0;
gboolean valid = TRUE;
relaxng_ctx_cache_t *ctx = NULL;
CRM_CHECK(doc != NULL, return FALSE);
CRM_CHECK(relaxng_file != NULL, return FALSE);
if(cached_ctx && *cached_ctx) {
ctx = *cached_ctx;
} else {
crm_info("Creating RNG parser context");
ctx = calloc(1, sizeof(relaxng_ctx_cache_t));
xmlLoadExtDtdDefaultValue = 1;
ctx->parser = xmlRelaxNGNewParserCtxt(relaxng_file);
CRM_CHECK(ctx->parser != NULL, goto cleanup);
if(to_logs) {
xmlRelaxNGSetParserErrors(ctx->parser,
(xmlRelaxNGValidityErrorFunc) xml_log,
(xmlRelaxNGValidityWarningFunc) xml_log,
GUINT_TO_POINTER(LOG_ERR));
} else {
xmlRelaxNGSetParserErrors(ctx->parser,
(xmlRelaxNGValidityErrorFunc) fprintf,
(xmlRelaxNGValidityWarningFunc) fprintf,
stderr);
}
ctx->rng = xmlRelaxNGParse(ctx->parser);
CRM_CHECK(ctx->rng != NULL, crm_err("Could not find/parse %s", relaxng_file); goto cleanup);
ctx->valid = xmlRelaxNGNewValidCtxt(ctx->rng);
CRM_CHECK(ctx->valid != NULL, goto cleanup);
if(to_logs) {
xmlRelaxNGSetValidErrors(ctx->valid,
(xmlRelaxNGValidityErrorFunc) xml_log,
(xmlRelaxNGValidityWarningFunc) xml_log,
GUINT_TO_POINTER(LOG_ERR));
} else {
xmlRelaxNGSetValidErrors(ctx->valid,
(xmlRelaxNGValidityErrorFunc) fprintf,
(xmlRelaxNGValidityWarningFunc) fprintf,
stderr);
}
}
/* xmlRelaxNGSetValidStructuredErrors( */
/* valid, relaxng_invalid_stderr, valid); */
xmlLineNumbersDefault(1);
rc = xmlRelaxNGValidateDoc(ctx->valid, doc);
if (rc > 0) {
valid = FALSE;
} else if (rc < 0) {
crm_err("Internal libxml error during validation\n");
}
cleanup:
if(cached_ctx) {
*cached_ctx = ctx;
} else {
if(ctx->parser != NULL) {
xmlRelaxNGFreeParserCtxt(ctx->parser);
}
if(ctx->valid != NULL) {
xmlRelaxNGFreeValidCtxt(ctx->valid);
}
if (ctx->rng != NULL) {
xmlRelaxNGFree(ctx->rng);
}
free(ctx);
}
return valid;
}
void crm_xml_cleanup(void)
{
int lpc = 0;
relaxng_ctx_cache_t *ctx = NULL;
crm_info("Cleaning up memory from libxml2");
for(; lpc < all_schemas; lpc++) {
switch(known_schemas[lpc].type) {
case 0:
/* None */
break;
case 1:
/* DTD - Not cached */
break;
case 2:
/* RNG - Cached */
ctx = (relaxng_ctx_cache_t *)known_schemas[lpc].cache;
if(ctx == NULL) {
break;
}
if(ctx->parser != NULL) {
xmlRelaxNGFreeParserCtxt(ctx->parser);
}
if(ctx->valid != NULL) {
xmlRelaxNGFreeValidCtxt(ctx->valid);
}
if (ctx->rng != NULL) {
xmlRelaxNGFree(ctx->rng);
}
free(ctx);
known_schemas[lpc].cache = NULL;
break;
default:
break;
}
}
xmlCleanupParser();
}
static gboolean validate_with(xmlNode *xml, int method, gboolean to_logs)
{
xmlDocPtr doc = NULL;
gboolean valid = FALSE;
int type = known_schemas[method].type;
char *file = NULL;
CRM_CHECK(xml != NULL, return FALSE);
doc = getDocPtr(xml);
file = get_schema_path(known_schemas[method].location);
crm_trace("Validating with: %s (type=%d)", crm_str(file), type);
switch(type) {
case 0:
valid = TRUE;
break;
case 1:
valid = validate_with_dtd(doc, to_logs, file);
break;
case 2:
valid = validate_with_relaxng(doc, to_logs, file, (relaxng_ctx_cache_t**)&(known_schemas[method].cache));
break;
default:
crm_err("Unknown validator type: %d", type);
break;
}
free(file);
return valid;
}
#include <stdio.h>
static void dump_file(const char *filename)
{
FILE *fp = NULL;
int ch, line = 0;
CRM_CHECK(filename != NULL, return);
fp = fopen(filename, "r");
CRM_CHECK(fp != NULL, return);
fprintf(stderr, "%4d ", ++line);
do {
ch = getc(fp);
if(ch == EOF) {
putc('\n', stderr);
break;
} else if(ch == '\n') {
fprintf(stderr, "\n%4d ", ++line);
} else {
putc(ch, stderr);
}
} while(1);
fclose(fp);
}
gboolean validate_xml_verbose(xmlNode *xml_blob)
{
xmlDoc *doc = NULL;
xmlNode *xml = NULL;
gboolean rc = FALSE;
char *filename = NULL;
static char *template = NULL;
if(template == NULL) {
template = crm_strdup(CRM_STATE_DIR"/cib-invalid.XXXXXX");
}
filename = mktemp(template);
write_xml_file(xml_blob, filename, FALSE);
dump_file(filename);
doc = xmlParseFile(filename);
xml = xmlDocGetRootElement(doc);
rc = validate_xml(xml, NULL, FALSE);
free_xml(xml);
return rc;
}
gboolean validate_xml(xmlNode *xml_blob, const char *validation, gboolean to_logs)
{
int lpc = 0;
if(validation == NULL) {
validation = crm_element_value(xml_blob, XML_ATTR_VALIDATION);
}
if(validation == NULL) {
validation = crm_element_value(xml_blob, "ignore-dtd");
if(crm_is_true(validation)) {
validation = "none";
} else {
validation = "pacemaker-1.0";
}
}
if(safe_str_eq(validation, "none")) {
return TRUE;
}
for(; lpc < all_schemas; lpc++) {
if(safe_str_eq(validation, known_schemas[lpc].name)) {
return validate_with(xml_blob, lpc, to_logs);
}
}
crm_err("Unknown validator: %s", validation);
return FALSE;
}
#if HAVE_LIBXSLT
static xmlNode *apply_transformation(xmlNode *xml, const char *transform)
{
char *xform = NULL;
xmlNode *out = NULL;
xmlDocPtr res = NULL;
xmlDocPtr doc = NULL;
xsltStylesheet *xslt = NULL;
CRM_CHECK(xml != NULL, return FALSE);
doc = getDocPtr(xml);
xform = get_schema_path(transform);
xmlLoadExtDtdDefaultValue = 1;
xmlSubstituteEntitiesDefault(1);
xslt = xsltParseStylesheetFile((const xmlChar *)xform);
CRM_CHECK(xslt != NULL, goto cleanup);
res = xsltApplyStylesheet(xslt, doc, NULL);
CRM_CHECK(res != NULL, goto cleanup);
out = xmlDocGetRootElement(res);
cleanup:
if(xslt) {
xsltFreeStylesheet(xslt);
}
xsltCleanupGlobals();
xmlCleanupParser();
free(xform);
return out;
}
#endif
const char *get_schema_name(int version)
{
if(version < 0 || version >= all_schemas) {
return "unknown";
}
return known_schemas[version].name;
}
int get_schema_version(const char *name)
{
int lpc = 0;
for(; lpc < all_schemas; lpc++) {
if(safe_str_eq(name, known_schemas[lpc].name)) {
return lpc;
}
}
return -1;
}
/* set which validation to use */
#include <crm/cib.h>
int update_validation(
xmlNode **xml_blob, int *best, gboolean transform, gboolean to_logs)
{
xmlNode *xml = NULL;
char *value = NULL;
int lpc = 0, match = -1, rc = cib_ok;
CRM_CHECK(best != NULL, return cib_invalid_argument);
CRM_CHECK(xml_blob != NULL, return cib_invalid_argument);
CRM_CHECK(*xml_blob != NULL, return cib_invalid_argument);
*best = 0;
xml = *xml_blob;
value = crm_element_value_copy(xml, XML_ATTR_VALIDATION);
if(value != NULL) {
match = get_schema_version(value);
lpc = match;
if(lpc >= 0 && transform == FALSE) {
lpc++;
} else if(lpc < 0) {
crm_debug("Unknown validation type");
lpc = 0;
}
}
if(match >= max_schemas) {
/* nothing to do */
free(value);
*best = match;
return cib_ok;
}
for(; lpc < max_schemas; lpc++) {
gboolean valid = TRUE;
crm_debug("Testing '%s' validation", known_schemas[lpc].name?known_schemas[lpc].name:"<unset>");
valid = validate_with(xml, lpc, to_logs);
if(valid) {
*best = lpc;
}
if(valid && transform) {
xmlNode *upgrade = NULL;
int next = known_schemas[lpc].after_transform;
if(next <= 0) {
next = lpc+1;
}
crm_notice("Upgrading %s-style configuration to %s with %s",
known_schemas[lpc].name, known_schemas[next].name, known_schemas[lpc].transform?known_schemas[lpc].transform:"no-op");
if(known_schemas[lpc].transform == NULL) {
if(validate_with(xml, next, to_logs)) {
crm_debug("Configuration valid for schema: %s", known_schemas[next].name);
lpc = next; *best = next;
rc = cib_ok;
} else {
crm_info("Configuration not valid for schema: %s", known_schemas[next].name);
}
} else {
#if HAVE_LIBXSLT
upgrade = apply_transformation(xml, known_schemas[lpc].transform);
#endif
if(upgrade == NULL) {
crm_err("Transformation %s failed", known_schemas[lpc].transform);
rc = cib_transform_failed;
} else if(validate_with(upgrade, next, to_logs)) {
crm_info("Transformation %s successful", known_schemas[lpc].transform);
lpc = next; *best = next;
free_xml(xml);
xml = upgrade;
rc = cib_ok;
} else {
crm_err("Transformation %s did not produce a valid configuration", known_schemas[lpc].transform);
crm_log_xml_info(upgrade, "transform:bad");
free_xml(upgrade);
rc = cib_dtd_validation;
}
}
}
}
if(*best > match) {
crm_notice("Upgraded from %s to %s validation", value?value:"<none>", known_schemas[*best].name);
crm_xml_add(xml, XML_ATTR_VALIDATION, known_schemas[*best].name);
}
*xml_blob = xml;
free(value);
return rc;
}
xmlNode *
getXpathResult(xmlXPathObjectPtr xpathObj, int index)
{
xmlNode *match = NULL;
CRM_CHECK(index >= 0, return NULL);
CRM_CHECK(xpathObj != NULL, return NULL);
if(index >= xpathObj->nodesetval->nodeNr) {
crm_err("Requested index %d of only %d items", index, xpathObj->nodesetval->nodeNr);
return NULL;
}
match = xpathObj->nodesetval->nodeTab[index];
CRM_CHECK(match != NULL, return NULL);
/*
* From xpath2.c
*
* All the elements returned by an XPath query are pointers to
* elements from the tree *except* namespace nodes where the XPath
* semantic is different from the implementation in libxml2 tree.
* As a result when a returned node set is freed when
* xmlXPathFreeObject() is called, that routine must check the
* element type. But node from the returned set may have been removed
* by xmlNodeSetContent() resulting in access to freed data.
* This can be exercised by running
* valgrind xpath2 test3.xml '//discarded' discarded
* There is 2 ways around it:
* - make a copy of the pointers to the nodes from the result set
* then call xmlXPathFreeObject() and then modify the nodes
* or
* - remove the reference to the modified nodes from the node set
* as they are processed, if they are not namespace nodes.
*/
if (xpathObj->nodesetval->nodeTab[index]->type != XML_NAMESPACE_DECL) {
xpathObj->nodesetval->nodeTab[index] = NULL;
}
if(match->type == XML_DOCUMENT_NODE) {
/* Will happen if section = '/' */
match = match->children;
} else if(match->type != XML_ELEMENT_NODE
&& match->parent
&& match->parent->type == XML_ELEMENT_NODE) {
/* reurning the parent instead */
match = match->parent;
} else if(match->type != XML_ELEMENT_NODE) {
/* We only support searching nodes */
crm_err("We only support %d not %d", XML_ELEMENT_NODE, match->type);
match = NULL;
}
return match;
}
/* the caller needs to check if the result contains a xmlDocPtr or xmlNodePtr */
xmlXPathObjectPtr
xpath_search(xmlNode *xml_top, const char *path)
{
xmlDocPtr doc = NULL;
xmlXPathObjectPtr xpathObj = NULL;
xmlXPathContextPtr xpathCtx = NULL;
const xmlChar *xpathExpr = (const xmlChar *)path;
CRM_CHECK(path != NULL, return NULL);
CRM_CHECK(xml_top != NULL, return NULL);
CRM_CHECK(strlen(path) > 0, return NULL);
doc = getDocPtr(xml_top);
crm_trace("Evaluating: %s", path);
xpathCtx = xmlXPathNewContext(doc);
CRM_ASSERT(xpathCtx != NULL);
xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx);
xmlXPathFreeContext(xpathCtx);
return xpathObj;
}
gboolean
cli_config_update(xmlNode **xml, int *best_version, gboolean to_logs)
{
gboolean rc = TRUE;
static int min_version = -1;
static int max_version = -1;
const char *value = crm_element_value(*xml, XML_ATTR_VALIDATION);
int version = get_schema_version(value);
if(min_version < 0) {
min_version = get_schema_version(MINIMUM_SCHEMA_VERSION);
}
if(max_version < 0) {
max_version = get_schema_version(LATEST_SCHEMA_VERSION);
}
if(version < min_version) {
xmlNode *converted = NULL;
converted = copy_xml(*xml);
update_validation(&converted, &version, TRUE, to_logs);
value = crm_element_value(converted, XML_ATTR_VALIDATION);
if(version < min_version) {
if(to_logs) {
crm_config_err("Your current configuration could only be upgraded to %s... "
"the minimum requirement is %s.\n", crm_str(value), MINIMUM_SCHEMA_VERSION);
} else {
fprintf(stderr, "Your current configuration could only be upgraded to %s... "
"the minimum requirement is %s.\n", crm_str(value), MINIMUM_SCHEMA_VERSION);
}
free_xml(converted);
converted = NULL;
rc = FALSE;
} else {
free_xml(*xml);
*xml = converted;
if(version < max_version) {
crm_config_warn("Your configuration was internally updated to %s... "
"which is acceptable but not the most recent",
get_schema_name(version));
} else if(to_logs){
crm_info("Your configuration was internally updated to the latest version (%s)",
get_schema_name(version));
}
}
} else if(version > max_version) {
if(to_logs){
crm_config_warn("Configuration validation is currently disabled."
" It is highly encouraged and prevents many common cluster issues.");
} else {
fprintf(stderr, "Configuration validation is currently disabled."
" It is highly encouraged and prevents many common cluster issues.\n");
}
}
if(best_version) {
*best_version = version;
}
return rc;
}
xmlNode *expand_idref(xmlNode *input, xmlNode *top)
{
const char *tag = NULL;
const char *ref = NULL;
xmlNode *result = input;
char *xpath_string = NULL;
if(result == NULL) {
return NULL;
} else if(top == NULL) {
top = input;
}
tag = crm_element_name(result);
ref = crm_element_value(result, XML_ATTR_IDREF);
if(ref != NULL) {
int xpath_max = 512, offset = 0;
xpath_string = calloc(1, xpath_max);
offset += snprintf(xpath_string + offset, xpath_max - offset, "//%s[@id='%s']", tag, ref);
result = get_xpath_object(xpath_string, top, LOG_ERR);
if(result == NULL) {
char *nodePath = (char *)xmlGetNodePath(top);
crm_err("No match for %s found in %s: Invalid configuration", xpath_string, crm_str(nodePath));
free(nodePath);
}
}
free(xpath_string);
return result;
}
xmlNode*
get_xpath_object_relative(const char *xpath, xmlNode *xml_obj, int error_level)
{
int len = 0;
xmlNode *result = NULL;
char *xpath_full = NULL;
char *xpath_prefix = NULL;
if(xml_obj == NULL || xpath == NULL) {
return NULL;
}
xpath_prefix = (char *)xmlGetNodePath(xml_obj);
len += strlen(xpath_prefix);
len += strlen(xpath);
xpath_full = crm_strdup(xpath_prefix);
crm_realloc(xpath_full, len+1);
strncat(xpath_full, xpath, len);
result = get_xpath_object(xpath_full, xml_obj, error_level);
free(xpath_prefix);
free(xpath_full);
return result;
}
xmlNode*
get_xpath_object(const char *xpath, xmlNode *xml_obj, int error_level)
{
xmlNode *result = NULL;
xmlXPathObjectPtr xpathObj = NULL;
char *nodePath = NULL;
char *matchNodePath = NULL;
if(xpath == NULL) {
return xml_obj; /* or return NULL? */
}
xpathObj = xpath_search(xml_obj, xpath);
nodePath = (char *)xmlGetNodePath(xml_obj);
if(xpathObj == NULL || xpathObj->nodesetval == NULL || xpathObj->nodesetval->nodeNr < 1) {
do_crm_log(error_level, "No match for %s in %s", xpath, crm_str(nodePath));
crm_log_xml_trace(xml_obj, "Unexpected Input");
} else if(xpathObj->nodesetval->nodeNr > 1) {
int lpc = 0, max = xpathObj->nodesetval->nodeNr;
do_crm_log(error_level, "Too many matches for %s in %s", xpath, crm_str(nodePath));
for(lpc = 0; lpc < max; lpc++) {
xmlNode *match = getXpathResult(xpathObj, lpc);
CRM_CHECK(match != NULL, continue);
matchNodePath = (char *)xmlGetNodePath(match);
do_crm_log(error_level, "%s[%d] = %s", xpath, lpc, crm_str(matchNodePath));
free(matchNodePath);
}
crm_log_xml_trace(xml_obj, "Bad Input");
} else {
result = getXpathResult(xpathObj, 0);
}
if(xpathObj) {
xmlXPathFreeObject(xpathObj);
}
free(nodePath);
return result;
}
const char *
crm_element_value(xmlNode *data, const char *name)
{
xmlAttr *attr = NULL;
if(data == NULL) {
crm_err("Couldn't find %s in NULL", name?name:"<null>");
return NULL;
} else if(name == NULL) {
crm_err("Couldn't find NULL in %s", crm_element_name(data));
return NULL;
}
attr = xmlHasProp(data, (const xmlChar*)name);
if(attr == NULL || attr->children == NULL) {
return NULL;
}
return (const char*)attr->children->content;
}
diff --git a/lib/pengine/rules.c b/lib/pengine/rules.c
index 8fc2b1297f..7526373faf 100644
--- a/lib/pengine/rules.c
+++ b/lib/pengine/rules.c
@@ -1,660 +1,660 @@
/*
* 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 <glib.h>
#include <crm/pengine/rules.h>
CRM_TRACE_INIT_DATA(pe_rules);
ha_time_t *parse_xml_duration(ha_time_t * start, xmlNode * duration_spec);
gboolean test_date_expression(xmlNode * time_expr, ha_time_t * now);
gboolean cron_range_satisfied(ha_time_t * now, xmlNode * cron_spec);
gboolean test_attr_expression(xmlNode * expr, GHashTable * hash, ha_time_t * now);
gboolean test_role_expression(xmlNode * expr, enum rsc_role_e role, ha_time_t * now);
gboolean
test_ruleset(xmlNode * ruleset, GHashTable * node_hash, ha_time_t * now)
{
gboolean ruleset_default = TRUE;
xmlNode *rule = NULL;
for (rule = __xml_first_child(ruleset); rule != NULL; rule = __xml_next(rule)) {
if (crm_str_eq((const char *)rule->name, XML_TAG_RULE, TRUE)) {
ruleset_default = FALSE;
if (test_rule(rule, node_hash, RSC_ROLE_UNKNOWN, now)) {
return TRUE;
}
}
}
return ruleset_default;
}
gboolean
test_rule(xmlNode * rule, GHashTable * node_hash, enum rsc_role_e role, ha_time_t * now)
{
xmlNode *expr = NULL;
gboolean test = TRUE;
gboolean empty = TRUE;
gboolean passed = TRUE;
gboolean do_and = TRUE;
const char *value = NULL;
rule = expand_idref(rule, NULL);
value = crm_element_value(rule, XML_RULE_ATTR_BOOLEAN_OP);
if (safe_str_eq(value, "or")) {
do_and = FALSE;
passed = FALSE;
}
crm_trace("Testing rule %s", ID(rule));
for (expr = __xml_first_child(rule); expr != NULL; expr = __xml_next(expr)) {
test = test_expression(expr, node_hash, role, now);
empty = FALSE;
if (test && do_and == FALSE) {
crm_trace("Expression %s/%s passed", ID(rule), ID(expr));
return TRUE;
} else if (test == FALSE && do_and) {
crm_trace("Expression %s/%s failed", ID(rule), ID(expr));
return FALSE;
}
}
if (empty) {
crm_err("Invalid Rule %s: rules must contain at least one expression", ID(rule));
}
crm_trace("Rule %s %s", ID(rule), passed ? "passed" : "failed");
return passed;
}
gboolean
test_expression(xmlNode * expr, GHashTable * node_hash, enum rsc_role_e role, ha_time_t * now)
{
gboolean accept = FALSE;
const char *uname = NULL;
switch (find_expression_type(expr)) {
case nested_rule:
accept = test_rule(expr, node_hash, role, now);
break;
case attr_expr:
case loc_expr:
/* these expressions can never succeed if there is
* no node to compare with
*/
if (node_hash != NULL) {
accept = test_attr_expression(expr, node_hash, now);
}
break;
case time_expr:
accept = test_date_expression(expr, now);
break;
case role_expr:
accept = test_role_expression(expr, role, now);
break;
default:
CRM_CHECK(FALSE /* bad type */ , return FALSE);
accept = FALSE;
}
if (node_hash) {
uname = g_hash_table_lookup(node_hash, "#uname");
}
crm_trace("Expression %s %s on %s",
ID(expr), accept ? "passed" : "failed", uname ? uname : "all ndoes");
return accept;
}
enum expression_type
find_expression_type(xmlNode * expr)
{
const char *tag = NULL;
const char *attr = NULL;
attr = crm_element_value(expr, XML_EXPR_ATTR_ATTRIBUTE);
tag = crm_element_name(expr);
if (safe_str_eq(tag, "date_expression")) {
return time_expr;
} else if (safe_str_eq(tag, XML_TAG_RULE)) {
return nested_rule;
} else if (safe_str_neq(tag, "expression")) {
return not_expr;
} else if (safe_str_eq(attr, "#uname") || safe_str_eq(attr, "#id")) {
return loc_expr;
} else if (safe_str_eq(attr, "#role")) {
return role_expr;
}
return attr_expr;
}
gboolean
test_role_expression(xmlNode * expr, enum rsc_role_e role, ha_time_t * now)
{
gboolean accept = FALSE;
const char *op = NULL;
const char *value = NULL;
if (role == RSC_ROLE_UNKNOWN) {
return accept;
}
value = crm_element_value(expr, XML_EXPR_ATTR_VALUE);
op = crm_element_value(expr, XML_EXPR_ATTR_OPERATION);
if (safe_str_eq(op, "defined")) {
if (role > RSC_ROLE_STARTED) {
accept = TRUE;
}
} else if (safe_str_eq(op, "not_defined")) {
if (role < RSC_ROLE_SLAVE && role > RSC_ROLE_UNKNOWN) {
accept = TRUE;
}
} else if (safe_str_eq(op, "eq")) {
if (text2role(value) == role) {
accept = TRUE;
}
} else if (safe_str_eq(op, "ne")) {
/* we will only test "ne" wtih master/slave roles style */
if (role < RSC_ROLE_SLAVE && role > RSC_ROLE_UNKNOWN) {
accept = FALSE;
} else if (text2role(value) != role) {
accept = TRUE;
}
}
return accept;
}
gboolean
test_attr_expression(xmlNode * expr, GHashTable * hash, ha_time_t * now)
{
gboolean accept = FALSE;
int cmp = 0;
const char *h_val = NULL;
const char *op = NULL;
const char *type = NULL;
const char *attr = NULL;
const char *value = NULL;
attr = crm_element_value(expr, XML_EXPR_ATTR_ATTRIBUTE);
op = crm_element_value(expr, XML_EXPR_ATTR_OPERATION);
value = crm_element_value(expr, XML_EXPR_ATTR_VALUE);
type = crm_element_value(expr, XML_EXPR_ATTR_TYPE);
if (attr == NULL || op == NULL) {
pe_err("Invlaid attribute or operation in expression"
" (\'%s\' \'%s\' \'%s\')", crm_str(attr), crm_str(op), crm_str(value));
return FALSE;
}
if (hash != NULL) {
h_val = (const char *)g_hash_table_lookup(hash, attr);
}
if (value != NULL && h_val != NULL) {
if (type == NULL) {
if (safe_str_eq(op, "lt")
|| safe_str_eq(op, "lte")
|| safe_str_eq(op, "gt")
|| safe_str_eq(op, "gte")) {
type = "number";
} else {
type = "string";
}
crm_trace("Defaulting to %s based comparison for '%s' op", type, op);
}
if (safe_str_eq(type, "string")) {
cmp = strcasecmp(h_val, value);
} else if (safe_str_eq(type, "number")) {
int h_val_f = crm_parse_int(h_val, NULL);
int value_f = crm_parse_int(value, NULL);
if (h_val_f < value_f) {
cmp = -1;
} else if (h_val_f > value_f) {
cmp = 1;
} else {
cmp = 0;
}
} else if (safe_str_eq(type, "version")) {
cmp = compare_version(h_val, value);
}
} else if (value == NULL && h_val == NULL) {
cmp = 0;
} else if (value == NULL) {
cmp = 1;
} else {
cmp = -1;
}
if (safe_str_eq(op, "defined")) {
if (h_val != NULL) {
accept = TRUE;
}
} else if (safe_str_eq(op, "not_defined")) {
if (h_val == NULL) {
accept = TRUE;
}
} else if (safe_str_eq(op, "eq")) {
if ((h_val == value) || cmp == 0) {
accept = TRUE;
}
} else if (safe_str_eq(op, "ne")) {
if ((h_val == NULL && value != NULL)
|| (h_val != NULL && value == NULL)
|| cmp != 0) {
accept = TRUE;
}
} else if (value == NULL || h_val == NULL) {
/* the comparision is meaningless from this point on */
accept = FALSE;
} else if (safe_str_eq(op, "lt")) {
if (cmp < 0) {
accept = TRUE;
}
} else if (safe_str_eq(op, "lte")) {
if (cmp <= 0) {
accept = TRUE;
}
} else if (safe_str_eq(op, "gt")) {
if (cmp > 0) {
accept = TRUE;
}
} else if (safe_str_eq(op, "gte")) {
if (cmp >= 0) {
accept = TRUE;
}
}
return accept;
}
/* As per the nethack rules:
*
* moon period = 29.53058 days ~= 30, year = 365.2422 days
* days moon phase advances on first day of year compared to preceding year
* = 365.2422 - 12*29.53058 ~= 11
* years in Metonic cycle (time until same phases fall on the same days of
* the month) = 18.6 ~= 19
* moon phase on first day of year (epact) ~= (11*(year%19) + 29) % 30
* (29 as initial condition)
* current phase in days = first day phase + days elapsed in year
* 6 moons ~= 177 days
* 177 ~= 8 reported phases * 22
* + 11/22 for rounding
*
* 0-7, with 0: new, 4: full
*/
static int
phase_of_the_moon(ha_time_t * now)
{
int epact, diy, goldn;
diy = now->yeardays;
goldn = (now->years % 19) + 1;
epact = (11 * goldn + 18) % 30;
if ((epact == 25 && goldn > 11) || epact == 24)
epact++;
return ((((((diy + epact) * 6) + 11) % 177) / 22) & 7);
}
#define cron_check(xml_field, time_field) \
value = crm_element_value(cron_spec, xml_field); \
if(value != NULL) { \
gboolean pass = TRUE; \
decodeNVpair(value, '-', &value_low, &value_high); \
if(value_low == NULL) { \
value_low = crm_strdup(value); \
} \
value_low_i = crm_parse_int(value_low, "0"); \
value_high_i = crm_parse_int(value_high, "-1"); \
if(value_high_i < 0) { \
if(value_low_i != time_field) { \
pass = FALSE; \
} \
} else if(value_low_i > time_field) { \
pass = FALSE; \
} else if(value_high_i < time_field) { \
pass = FALSE; \
} \
free(value_low); \
free(value_high); \
if(pass == FALSE) { \
crm_debug("Condition '%s' in %s: failed", value, xml_field); \
return pass; \
} \
crm_debug("Condition '%s' in %s: passed", value, xml_field); \
}
gboolean
cron_range_satisfied(ha_time_t * now, xmlNode * cron_spec)
{
const char *value = NULL;
char *value_low = NULL;
char *value_high = NULL;
int value_low_i = 0;
int value_high_i = 0;
CRM_CHECK(now != NULL, return FALSE);
cron_check("seconds", now->seconds);
cron_check("minutes", now->minutes);
cron_check("hours", now->hours);
cron_check("monthdays", now->days);
cron_check("weekdays", now->weekdays);
cron_check("yeardays", now->yeardays);
cron_check("weeks", now->weeks);
cron_check("months", now->months);
cron_check("years", now->years);
cron_check("weekyears", now->weekyears);
cron_check("moon", phase_of_the_moon(now));
return TRUE;
}
#define update_field(xml_field, time_fn) \
value = crm_element_value(duration_spec, xml_field); \
if(value != NULL) { \
int value_i = crm_parse_int(value, "0"); \
time_fn(end, value_i); \
}
ha_time_t *
parse_xml_duration(ha_time_t * start, xmlNode * duration_spec)
{
ha_time_t *end = NULL;
const char *value = NULL;
end = new_ha_date(FALSE);
ha_set_time(end, start, TRUE);
update_field("years", add_years);
update_field("months", add_months);
update_field("weeks", add_weeks);
update_field("days", add_days);
update_field("hours", add_hours);
update_field("minutes", add_minutes);
update_field("seconds", add_seconds);
return end;
}
gboolean
test_date_expression(xmlNode * time_expr, ha_time_t * now)
{
ha_time_t *start = NULL;
ha_time_t *end = NULL;
const char *value = NULL;
char *value_copy = NULL;
char *value_copy_start = NULL;
const char *op = crm_element_value(time_expr, "operation");
xmlNode *duration_spec = NULL;
xmlNode *date_spec = NULL;
gboolean passed = FALSE;
crm_trace("Testing expression: %s", ID(time_expr));
duration_spec = first_named_child(time_expr, "duration");
date_spec = first_named_child(time_expr, "date_spec");
value = crm_element_value(time_expr, "start");
if (value != NULL) {
value_copy = crm_strdup(value);
value_copy_start = value_copy;
start = parse_date(&value_copy);
free(value_copy_start);
}
value = crm_element_value(time_expr, "end");
if (value != NULL) {
value_copy = crm_strdup(value);
value_copy_start = value_copy;
end = parse_date(&value_copy);
free(value_copy_start);
}
if (start != NULL && end == NULL && duration_spec != NULL) {
end = parse_xml_duration(start, duration_spec);
}
if (op == NULL) {
op = "in_range";
}
if (safe_str_eq(op, "date_spec") || safe_str_eq(op, "in_range")) {
if (start != NULL && compare_date(start, now) > 0) {
passed = FALSE;
} else if (end != NULL && compare_date(end, now) < 0) {
passed = FALSE;
} else if (safe_str_eq(op, "in_range")) {
passed = TRUE;
} else {
passed = cron_range_satisfied(now, date_spec);
}
} else if (safe_str_eq(op, "gt") && compare_date(start, now) < 0) {
passed = TRUE;
} else if (safe_str_eq(op, "lt") && compare_date(end, now) > 0) {
passed = TRUE;
} else if (safe_str_eq(op, "eq") && compare_date(start, now) == 0) {
passed = TRUE;
} else if (safe_str_eq(op, "neq") && compare_date(start, now) != 0) {
passed = TRUE;
}
free_ha_date(start);
free_ha_date(end);
return passed;
}
typedef struct sorted_set_s {
int score;
const char *name;
const char *special_name;
xmlNode *attr_set;
} sorted_set_t;
static gint
sort_pairs(gconstpointer a, gconstpointer b)
{
const sorted_set_t *pair_a = a;
const sorted_set_t *pair_b = b;
if (a == NULL && b == NULL) {
return 0;
} else if (a == NULL) {
return 1;
} else if (b == NULL) {
return -1;
}
if (safe_str_eq(pair_a->name, pair_a->special_name)) {
return -1;
} else if (safe_str_eq(pair_b->name, pair_a->special_name)) {
return 1;
}
if (pair_a->score < pair_b->score) {
return 1;
} else if (pair_a->score > pair_b->score) {
return -1;
}
return 0;
}
static void
populate_hash(xmlNode * nvpair_list, GHashTable * hash, gboolean overwrite)
{
const char *name = NULL;
const char *value = NULL;
const char *old_value = NULL;
xmlNode *list = nvpair_list;
xmlNode *an_attr = NULL;
name = crm_element_name(list->children);
if (safe_str_eq(XML_TAG_ATTRS, name)) {
list = list->children;
}
for (an_attr = __xml_first_child(list); an_attr != NULL; an_attr = __xml_next(an_attr)) {
if (crm_str_eq((const char *)an_attr->name, XML_CIB_TAG_NVPAIR, TRUE)) {
name = crm_element_value(an_attr, XML_NVPAIR_ATTR_NAME);
crm_trace("Setting attribute: %s", name);
value = crm_element_value(an_attr, XML_NVPAIR_ATTR_VALUE);
if (name == NULL || value == NULL) {
continue;
}
old_value = g_hash_table_lookup(hash, name);
if (safe_str_eq(value, "#default")) {
if (old_value) {
crm_trace("Removing value for %s (%s)", name, value);
g_hash_table_remove(hash, name);
}
continue;
} else if (old_value == NULL) {
g_hash_table_insert(hash, crm_strdup(name), crm_strdup(value));
} else if (overwrite) {
crm_debug("Overwriting value of %s: %s -> %s", name, old_value, value);
g_hash_table_replace(hash, crm_strdup(name), crm_strdup(value));
}
}
}
}
struct unpack_data_s {
gboolean overwrite;
GHashTable *node_hash;
GHashTable *hash;
ha_time_t *now;
};
static void
unpack_attr_set(gpointer data, gpointer user_data)
{
sorted_set_t *pair = data;
struct unpack_data_s *unpack_data = user_data;
if (test_ruleset(pair->attr_set, unpack_data->node_hash, unpack_data->now) == FALSE) {
return;
}
crm_trace("Adding attributes from %s", pair->name);
populate_hash(pair->attr_set, unpack_data->hash, unpack_data->overwrite);
}
void
unpack_instance_attributes(xmlNode * top, xmlNode * xml_obj, const char *set_name,
GHashTable * node_hash, GHashTable * hash, const char *always_first,
gboolean overwrite, ha_time_t * now)
{
GListPtr sorted = NULL;
GListPtr unsorted = NULL;
const char *score = NULL;
sorted_set_t *pair = NULL;
struct unpack_data_s data;
xmlNode *attr_set = NULL;
if (xml_obj == NULL) {
crm_trace("No instance attributes");
return;
}
crm_trace("Checking for attributes");
for (attr_set = __xml_first_child(xml_obj); attr_set != NULL; attr_set = __xml_next(attr_set)) {
/* Uncertain if set_name == NULL check is strictly necessary here */
if (set_name == NULL || crm_str_eq((const char *)attr_set->name, set_name, TRUE)) {
pair = NULL;
attr_set = expand_idref(attr_set, top);
if (attr_set == NULL) {
continue;
}
pair = calloc(1, sizeof(sorted_set_t));
pair->name = ID(attr_set);
pair->special_name = always_first;
pair->attr_set = attr_set;
score = crm_element_value(attr_set, XML_RULE_ATTR_SCORE);
pair->score = char2score(score);
unsorted = g_list_prepend(unsorted, pair);
}
}
if (pair != NULL) {
data.hash = hash;
data.node_hash = node_hash;
data.now = now;
data.overwrite = overwrite;
}
sorted = g_list_sort(unsorted, sort_pairs);
g_list_foreach(sorted, unpack_attr_set, &data);
- slist_basic_destroy(sorted);
+ g_list_free_full(sorted, free);
}
diff --git a/lib/pengine/unpack.c b/lib/pengine/unpack.c
index cada7ff136..01356e795c 100644
--- a/lib/pengine/unpack.c
+++ b/lib/pengine/unpack.c
@@ -1,2408 +1,2408 @@
/*
* 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 <glib.h>
#include <crm/crm.h>
#include <crm/services.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/util.h>
#include <crm/pengine/status.h>
#include <crm/pengine/rules.h>
#include <utils.h>
#include <unpack.h>
CRM_TRACE_INIT_DATA(pe_status);
#define set_config_flag(data_set, option, flag) do { \
const char *tmp = pe_pref(data_set->config_hash, option); \
if(tmp) { \
if(crm_is_true(tmp)) { \
set_bit_inplace(data_set->flags, flag); \
} else { \
clear_bit_inplace(data_set->flags, flag); \
} \
} \
} while(0)
gboolean unpack_rsc_op(resource_t * rsc, node_t * node, xmlNode * xml_op, GListPtr next,
enum action_fail_response *failed, pe_working_set_t * data_set);
static void
pe_fence_node(pe_working_set_t * data_set, node_t * node, const char *reason)
{
CRM_CHECK(node, return);
if (node->details->unclean == FALSE) {
if (is_set(data_set->flags, pe_flag_stonith_enabled)) {
crm_warn("Node %s will be fenced %s", node->details->uname, reason);
} else {
crm_warn("Node %s is unclean %s", node->details->uname, reason);
}
}
node->details->unclean = TRUE;
}
gboolean
unpack_config(xmlNode * config, pe_working_set_t * data_set)
{
const char *value = NULL;
GHashTable *config_hash =
g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str);
data_set->config_hash = config_hash;
unpack_instance_attributes(data_set->input, config, XML_CIB_TAG_PROPSET, NULL, config_hash,
CIB_OPTIONS_FIRST, FALSE, data_set->now);
verify_pe_options(data_set->config_hash);
set_config_flag(data_set, "enable-startup-probes", pe_flag_startup_probes);
crm_info("Startup probes: %s",
is_set(data_set->flags, pe_flag_startup_probes) ? "enabled" : "disabled (dangerous)");
value = pe_pref(data_set->config_hash, "stonith-timeout");
data_set->stonith_timeout = crm_get_msec(value);
crm_debug("STONITH timeout: %d", data_set->stonith_timeout);
set_config_flag(data_set, "stonith-enabled", pe_flag_stonith_enabled);
crm_debug("STONITH of failed nodes is %s",
is_set(data_set->flags, pe_flag_stonith_enabled) ? "enabled" : "disabled");
data_set->stonith_action = pe_pref(data_set->config_hash, "stonith-action");
crm_trace("STONITH will %s nodes", data_set->stonith_action);
set_config_flag(data_set, "stop-all-resources", pe_flag_stop_everything);
crm_debug("Stop all active resources: %s",
is_set(data_set->flags, pe_flag_stop_everything) ? "true" : "false");
set_config_flag(data_set, "symmetric-cluster", pe_flag_symmetric_cluster);
if (is_set(data_set->flags, pe_flag_symmetric_cluster)) {
crm_debug("Cluster is symmetric" " - resources can run anywhere by default");
}
value = pe_pref(data_set->config_hash, "default-resource-stickiness");
data_set->default_resource_stickiness = char2score(value);
crm_debug("Default stickiness: %d", data_set->default_resource_stickiness);
value = pe_pref(data_set->config_hash, "no-quorum-policy");
if (safe_str_eq(value, "ignore")) {
data_set->no_quorum_policy = no_quorum_ignore;
} else if (safe_str_eq(value, "freeze")) {
data_set->no_quorum_policy = no_quorum_freeze;
} else if (safe_str_eq(value, "suicide")) {
gboolean do_panic = FALSE;
crm_element_value_int(data_set->input, XML_ATTR_QUORUM_PANIC, &do_panic);
if (is_set(data_set->flags, pe_flag_stonith_enabled) == FALSE) {
crm_config_err
("Setting no-quorum-policy=suicide makes no sense if stonith-enabled=false");
}
if (do_panic && is_set(data_set->flags, pe_flag_stonith_enabled)) {
data_set->no_quorum_policy = no_quorum_suicide;
} else if (is_set(data_set->flags, pe_flag_have_quorum) == FALSE && do_panic == FALSE) {
crm_notice("Resetting no-quorum-policy to 'stop': The cluster has never had quorum");
data_set->no_quorum_policy = no_quorum_stop;
}
} else {
data_set->no_quorum_policy = no_quorum_stop;
}
switch (data_set->no_quorum_policy) {
case no_quorum_freeze:
crm_debug("On loss of CCM Quorum: Freeze resources");
break;
case no_quorum_stop:
crm_debug("On loss of CCM Quorum: Stop ALL resources");
break;
case no_quorum_suicide:
crm_notice("On loss of CCM Quorum: Fence all remaining nodes");
break;
case no_quorum_ignore:
crm_notice("On loss of CCM Quorum: Ignore");
break;
}
set_config_flag(data_set, "stop-orphan-resources", pe_flag_stop_rsc_orphans);
crm_trace("Orphan resources are %s",
is_set(data_set->flags, pe_flag_stop_rsc_orphans) ? "stopped" : "ignored");
set_config_flag(data_set, "stop-orphan-actions", pe_flag_stop_action_orphans);
crm_trace("Orphan resource actions are %s",
is_set(data_set->flags, pe_flag_stop_action_orphans) ? "stopped" : "ignored");
set_config_flag(data_set, "remove-after-stop", pe_flag_remove_after_stop);
crm_trace("Stopped resources are removed from the status section: %s",
is_set(data_set->flags, pe_flag_remove_after_stop) ? "true" : "false");
set_config_flag(data_set, "maintenance-mode", pe_flag_maintenance_mode);
crm_trace("Maintenance mode: %s",
is_set(data_set->flags, pe_flag_maintenance_mode) ? "true" : "false");
if (is_set(data_set->flags, pe_flag_maintenance_mode)) {
clear_bit(data_set->flags, pe_flag_is_managed_default);
} else {
set_config_flag(data_set, "is-managed-default", pe_flag_is_managed_default);
}
crm_trace("By default resources are %smanaged",
is_set(data_set->flags, pe_flag_is_managed_default) ? "" : "not ");
set_config_flag(data_set, "start-failure-is-fatal", pe_flag_start_failure_fatal);
crm_trace("Start failures are %s",
is_set(data_set->flags,
pe_flag_start_failure_fatal) ? "always fatal" : "handled by failcount");
node_score_red = char2score(pe_pref(data_set->config_hash, "node-health-red"));
node_score_green = char2score(pe_pref(data_set->config_hash, "node-health-green"));
node_score_yellow = char2score(pe_pref(data_set->config_hash, "node-health-yellow"));
crm_info("Node scores: 'red' = %s, 'yellow' = %s, 'green' = %s",
pe_pref(data_set->config_hash, "node-health-red"),
pe_pref(data_set->config_hash, "node-health-yellow"),
pe_pref(data_set->config_hash, "node-health-green"));
data_set->placement_strategy = pe_pref(data_set->config_hash, "placement-strategy");
crm_trace("Placement strategy: %s", data_set->placement_strategy);
return TRUE;
}
gboolean
unpack_nodes(xmlNode * xml_nodes, pe_working_set_t * data_set)
{
xmlNode *xml_obj = NULL;
node_t *new_node = NULL;
const char *id = NULL;
const char *uname = NULL;
const char *type = NULL;
const char *score = NULL;
gboolean unseen_are_unclean = TRUE;
const char *blind_faith = pe_pref(data_set->config_hash, "startup-fencing");
if (crm_is_true(blind_faith) == FALSE) {
unseen_are_unclean = FALSE;
crm_warn("Blind faith: not fencing unseen nodes");
}
for (xml_obj = __xml_first_child(xml_nodes); xml_obj != NULL; xml_obj = __xml_next(xml_obj)) {
if (crm_str_eq((const char *)xml_obj->name, XML_CIB_TAG_NODE, TRUE)) {
new_node = NULL;
id = crm_element_value(xml_obj, XML_ATTR_ID);
uname = crm_element_value(xml_obj, XML_ATTR_UNAME);
type = crm_element_value(xml_obj, XML_ATTR_TYPE);
score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
crm_trace("Processing node %s/%s", uname, id);
if (id == NULL) {
crm_config_err("Must specify id tag in <node>");
continue;
}
if (type == NULL) {
crm_config_err("Must specify type tag in <node>");
continue;
}
if (pe_find_node(data_set->nodes, uname) != NULL) {
crm_config_warn("Detected multiple node entries with uname=%s"
" - this is rarely intended", uname);
}
new_node = calloc(1, sizeof(node_t));
if (new_node == NULL) {
return FALSE;
}
new_node->weight = char2score(score);
new_node->fixed = FALSE;
new_node->details = calloc(1, sizeof(struct node_shared_s));
if (new_node->details == NULL) {
free(new_node);
return FALSE;
}
crm_trace("Creaing node for entry %s/%s", uname, id);
new_node->details->id = id;
new_node->details->uname = uname;
new_node->details->type = node_ping;
new_node->details->online = FALSE;
new_node->details->shutdown = FALSE;
new_node->details->running_rsc = NULL;
new_node->details->attrs = g_hash_table_new_full(crm_str_hash, g_str_equal,
g_hash_destroy_str,
g_hash_destroy_str);
new_node->details->utilization =
g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str,
g_hash_destroy_str);
/* if(data_set->have_quorum == FALSE */
/* && data_set->no_quorum_policy == no_quorum_stop) { */
/* /\* start shutting resources down *\/ */
/* new_node->weight = -INFINITY; */
/* } */
if (is_set(data_set->flags, pe_flag_stonith_enabled) == FALSE
|| unseen_are_unclean == FALSE) {
/* blind faith... */
new_node->details->unclean = FALSE;
} else {
/* all nodes are unclean until we've seen their
* status entry
*/
new_node->details->unclean = TRUE;
}
if (type == NULL || safe_str_eq(type, "member")
|| safe_str_eq(type, NORMALNODE)) {
new_node->details->type = node_member;
}
add_node_attrs(xml_obj, new_node, FALSE, data_set);
unpack_instance_attributes(data_set->input, xml_obj, XML_TAG_UTILIZATION, NULL,
new_node->details->utilization, NULL, FALSE, data_set->now);
data_set->nodes = g_list_append(data_set->nodes, new_node);
crm_trace("Done with node %s", crm_element_value(xml_obj, XML_ATTR_UNAME));
}
}
return TRUE;
}
static void
g_hash_destroy_node_list(gpointer data)
{
GListPtr domain = data;
- slist_basic_destroy(domain);
+ g_list_free_full(domain, free);
}
gboolean
unpack_domains(xmlNode * xml_domains, pe_working_set_t * data_set)
{
const char *id = NULL;
GListPtr domain = NULL;
xmlNode *xml_node = NULL;
xmlNode *xml_domain = NULL;
crm_info("Unpacking domains");
data_set->domains =
g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str,
g_hash_destroy_node_list);
for (xml_domain = __xml_first_child(xml_domains); xml_domain != NULL;
xml_domain = __xml_next(xml_domain)) {
if (crm_str_eq((const char *)xml_domain->name, XML_CIB_TAG_DOMAIN, TRUE)) {
domain = NULL;
id = crm_element_value(xml_domain, XML_ATTR_ID);
for (xml_node = __xml_first_child(xml_domain); xml_node != NULL;
xml_node = __xml_next(xml_node)) {
if (crm_str_eq((const char *)xml_node->name, XML_CIB_TAG_NODE, TRUE)) {
node_t *copy = NULL;
node_t *node = NULL;
const char *uname = crm_element_value(xml_node, "name");
const char *score = crm_element_value(xml_node, XML_RULE_ATTR_SCORE);
if (uname == NULL) {
crm_config_err("Invalid domain %s: Must specify id tag in <node>", id);
continue;
}
node = pe_find_node(data_set->nodes, uname);
if (node == NULL) {
node = pe_find_node_id(data_set->nodes, uname);
}
if (node == NULL) {
crm_config_warn("Invalid domain %s: Node %s does not exist", id, uname);
continue;
}
copy = node_copy(node);
copy->weight = char2score(score);
crm_debug("Adding %s to domain %s with score %s", node->details->uname, id,
score);
domain = g_list_prepend(domain, copy);
}
}
if (domain) {
crm_debug("Created domain %s with %d members", id, g_list_length(domain));
g_hash_table_replace(data_set->domains, crm_strdup(id), domain);
}
}
}
return TRUE;
}
static void
destroy_template_rsc_set(gpointer data)
{
xmlNode *rsc_set = data;
free_xml(rsc_set);
}
gboolean
unpack_resources(xmlNode * xml_resources, pe_working_set_t * data_set)
{
xmlNode *xml_obj = NULL;
data_set->template_rsc_sets =
g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str,
destroy_template_rsc_set);
for (xml_obj = __xml_first_child(xml_resources); xml_obj != NULL; xml_obj = __xml_next(xml_obj)) {
resource_t *new_rsc = NULL;
if (crm_str_eq((const char *)xml_obj->name, XML_CIB_TAG_RSC_TEMPLATE, TRUE)) {
const char *template_id = ID(xml_obj);
if (template_id && g_hash_table_lookup_extended(data_set->template_rsc_sets,
template_id, NULL, NULL) == FALSE) {
/* Record the template's ID for the knowledge of its existence anyway. */
g_hash_table_insert(data_set->template_rsc_sets, crm_strdup(template_id), NULL);
}
continue;
}
crm_trace("Beginning unpack... <%s id=%s... >", crm_element_name(xml_obj), ID(xml_obj));
if (common_unpack(xml_obj, &new_rsc, NULL, data_set)) {
data_set->resources = g_list_append(data_set->resources, new_rsc);
print_resource(LOG_DEBUG_3, "Added", new_rsc, FALSE);
} else {
crm_config_err("Failed unpacking %s %s",
crm_element_name(xml_obj), crm_element_value(xml_obj, XML_ATTR_ID));
if (new_rsc != NULL && new_rsc->fns != NULL) {
new_rsc->fns->free(new_rsc);
}
}
}
data_set->resources = g_list_sort(data_set->resources, sort_rsc_priority);
if (is_set(data_set->flags, pe_flag_stonith_enabled)
&& is_set(data_set->flags, pe_flag_have_stonith_resource) == FALSE) {
crm_config_err("Resource start-up disabled since no STONITH resources have been defined");
crm_config_err("Either configure some or disable STONITH with the stonith-enabled option");
crm_config_err("NOTE: Clusters with shared data need STONITH to ensure data integrity");
}
return TRUE;
}
/* The ticket state section:
* "/cib/status/tickets/ticket_state" */
static gboolean
unpack_ticket_state(xmlNode * xml_ticket, pe_working_set_t * data_set)
{
const char *ticket_id = NULL;
const char *granted = NULL;
const char *last_granted = NULL;
const char *standby = NULL;
xmlAttrPtr xIter = NULL;
ticket_t *ticket = NULL;
ticket_id = ID(xml_ticket);
if (ticket_id == NULL || strlen(ticket_id) == 0) {
return FALSE;
}
crm_trace("Processing ticket state for %s", ticket_id);
ticket = g_hash_table_lookup(data_set->tickets, ticket_id);
if (ticket == NULL) {
ticket = ticket_new(ticket_id, data_set);
if (ticket == NULL) {
return FALSE;
}
}
for (xIter = xml_ticket->properties; xIter; xIter = xIter->next) {
const char *prop_name = (const char *)xIter->name;
const char *prop_value = crm_element_value(xml_ticket, prop_name);
if(crm_str_eq(prop_name, XML_ATTR_ID, TRUE)) {
continue;
}
g_hash_table_replace(ticket->state, crm_strdup(prop_name), crm_strdup(prop_value));
}
granted = g_hash_table_lookup(ticket->state, "granted");
if (granted && crm_is_true(granted)) {
ticket->granted = TRUE;
crm_info("We have ticket '%s'", ticket->id);
} else {
ticket->granted = FALSE;
crm_info("We do not have ticket '%s'", ticket->id);
}
last_granted = g_hash_table_lookup(ticket->state, "last-granted");
if (last_granted) {
ticket->last_granted = crm_parse_int(last_granted, 0);
}
standby = g_hash_table_lookup(ticket->state, "standby");
if (standby && crm_is_true(standby)) {
ticket->standby = TRUE;
if (ticket->granted) {
crm_info("Granted ticket '%s' is in standby-mode", ticket->id);
}
} else {
ticket->standby = FALSE;
}
crm_trace("Done with ticket state for %s", ticket_id);
return TRUE;
}
static gboolean
unpack_tickets_state(xmlNode * xml_tickets, pe_working_set_t * data_set)
{
xmlNode *xml_obj = NULL;
for (xml_obj = __xml_first_child(xml_tickets); xml_obj != NULL; xml_obj = __xml_next(xml_obj)) {
if (crm_str_eq((const char *)xml_obj->name, XML_CIB_TAG_TICKET_STATE, TRUE) == FALSE) {
continue;
}
unpack_ticket_state(xml_obj, data_set);
}
return TRUE;
}
/* Compatibility with the deprecated ticket state section:
* "/cib/status/tickets/instance_attributes" */
static void
get_ticket_state_legacy(gpointer key, gpointer value, gpointer user_data)
{
const char *long_key = key;
char *state_key = NULL;
const char *granted_prefix = "granted-ticket-";
const char *last_granted_prefix = "last-granted-";
static int granted_prefix_strlen = 0;
static int last_granted_prefix_strlen = 0;
const char *ticket_id = NULL;
const char *is_granted = NULL;
const char *last_granted = NULL;
const char *sep = NULL;
ticket_t *ticket = NULL;
pe_working_set_t *data_set = user_data;
if (granted_prefix_strlen == 0) {
granted_prefix_strlen = strlen(granted_prefix);
}
if (last_granted_prefix_strlen == 0) {
last_granted_prefix_strlen = strlen(last_granted_prefix);
}
if (strstr(long_key, granted_prefix) == long_key) {
ticket_id = long_key + granted_prefix_strlen;
if (strlen(ticket_id)) {
state_key = crm_strdup("granted");
is_granted = value;
}
} else if (strstr(long_key, last_granted_prefix) == long_key) {
ticket_id = long_key + last_granted_prefix_strlen;
if (strlen(ticket_id)) {
state_key = crm_strdup("last-granted");
last_granted = value;
}
} else if ((sep = strrchr(long_key, '-'))) {
ticket_id = sep + 1;
state_key = strndup(long_key, strlen(long_key) - strlen(sep));
}
if (ticket_id == NULL || strlen(ticket_id) == 0) {
free(state_key);
return;
}
if (state_key == NULL || strlen(state_key) == 0) {
free(state_key);
return;
}
ticket = g_hash_table_lookup(data_set->tickets, ticket_id);
if (ticket == NULL) {
ticket = ticket_new(ticket_id, data_set);
if (ticket == NULL) {
free(state_key);
return;
}
}
g_hash_table_replace(ticket->state, state_key, crm_strdup(value));
if (is_granted) {
if (crm_is_true(is_granted)) {
ticket->granted = TRUE;
crm_info("We have ticket '%s'", ticket->id);
} else {
ticket->granted = FALSE;
crm_info("We do not have ticket '%s'", ticket->id);
}
} else if (last_granted) {
ticket->last_granted = crm_parse_int(last_granted, 0);
}
}
/* remove nodes that are down, stopping */
/* create +ve rsc_to_node constraints between resources and the nodes they are running on */
/* anything else? */
gboolean
unpack_status(xmlNode * status, pe_working_set_t * data_set)
{
const char *id = NULL;
const char *uname = NULL;
xmlNode *lrm_rsc = NULL;
xmlNode *attrs = NULL;
xmlNode *state = NULL;
xmlNode *node_state = NULL;
node_t *this_node = NULL;
crm_trace("Beginning unpack");
if (data_set->tickets == NULL) {
data_set->tickets =
g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, destroy_ticket);
}
for (state = __xml_first_child(status); state != NULL; state = __xml_next(state)) {
if (crm_str_eq((const char *)state->name, XML_CIB_TAG_TICKETS, TRUE)) {
xmlNode *xml_tickets = state;
GHashTable *state_hash = NULL;
/* Compatibility with the deprecated ticket state section:
* Unpack the attributes in the deprecated "/cib/status/tickets/instance_attributes" if it exists. */
state_hash = g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str,
g_hash_destroy_str);
unpack_instance_attributes(data_set->input, xml_tickets, XML_TAG_ATTR_SETS, NULL,
state_hash, NULL, TRUE, data_set->now);
g_hash_table_foreach(state_hash, get_ticket_state_legacy, data_set);
if (state_hash) {
g_hash_table_destroy(state_hash);
}
/* Unpack the new "/cib/status/tickets/ticket_state"s */
unpack_tickets_state(xml_tickets, data_set);
}
if (crm_str_eq((const char *)state->name, XML_CIB_TAG_STATE, TRUE)) {
node_state = state;
id = crm_element_value(node_state, XML_ATTR_ID);
uname = crm_element_value(node_state, XML_ATTR_UNAME);
attrs = find_xml_node(node_state, XML_TAG_TRANSIENT_NODEATTRS, FALSE);
crm_trace("Processing node id=%s, uname=%s", id, uname);
this_node = pe_find_node_id(data_set->nodes, id);
if (uname == NULL) {
/* error */
continue;
} else if (this_node == NULL) {
crm_config_warn("Node %s in status section no longer exists", uname);
continue;
}
/* Mark the node as provisionally clean
* - at least we have seen it in the current cluster's lifetime
*/
this_node->details->unclean = FALSE;
add_node_attrs(attrs, this_node, TRUE, data_set);
if (crm_is_true(g_hash_table_lookup(this_node->details->attrs, "standby"))) {
crm_info("Node %s is in standby-mode", this_node->details->uname);
this_node->details->standby = TRUE;
}
crm_trace("determining node state");
determine_online_status(node_state, this_node, data_set);
if (this_node->details->online && data_set->no_quorum_policy == no_quorum_suicide) {
/* Everything else should flow from this automatically
* At least until the PE becomes able to migrate off healthy resources
*/
pe_fence_node(data_set, this_node, "because the cluster does not have quorum");
}
}
}
/* Now that we know all node states, we can safely handle migration ops
* But, for now, only process healthy nodes
* - this is necessary for the logic in bug lf#2508 to function correctly
*/
for (node_state = __xml_first_child(status); node_state != NULL;
node_state = __xml_next(node_state)) {
if (crm_str_eq((const char *)node_state->name, XML_CIB_TAG_STATE, TRUE) == FALSE) {
continue;
}
id = crm_element_value(node_state, XML_ATTR_ID);
this_node = pe_find_node_id(data_set->nodes, id);
if (this_node == NULL) {
crm_info("Node %s is unknown", id);
continue;
} else if (this_node->details->online) {
crm_trace("Processing lrm resource entries on healthy node: %s",
this_node->details->uname);
lrm_rsc = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE);
lrm_rsc = find_xml_node(lrm_rsc, XML_LRM_TAG_RESOURCES, FALSE);
unpack_lrm_resources(this_node, lrm_rsc, data_set);
}
}
/* Now handle failed nodes - but only if stonith is enabled
*
* By definition, offline nodes run no resources so there is nothing to do.
* Only when stonith is enabled do we need to know what is on the node to
* ensure rsc start events happen after the stonith
*/
for (node_state = __xml_first_child(status);
node_state != NULL && is_set(data_set->flags, pe_flag_stonith_enabled);
node_state = __xml_next(node_state)) {
if (crm_str_eq((const char *)node_state->name, XML_CIB_TAG_STATE, TRUE) == FALSE) {
continue;
}
id = crm_element_value(node_state, XML_ATTR_ID);
this_node = pe_find_node_id(data_set->nodes, id);
if (this_node == NULL || this_node->details->online) {
continue;
} else {
crm_trace("Processing lrm resource entries on unhealthy node: %s",
this_node->details->uname);
lrm_rsc = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE);
lrm_rsc = find_xml_node(lrm_rsc, XML_LRM_TAG_RESOURCES, FALSE);
unpack_lrm_resources(this_node, lrm_rsc, data_set);
}
}
return TRUE;
}
static gboolean
determine_online_status_no_fencing(pe_working_set_t * data_set, xmlNode * node_state,
node_t * this_node)
{
gboolean online = FALSE;
const char *join_state = crm_element_value(node_state, XML_CIB_ATTR_JOINSTATE);
const char *crm_state = crm_element_value(node_state, XML_CIB_ATTR_CRMDSTATE);
const char *ccm_state = crm_element_value(node_state, XML_CIB_ATTR_INCCM);
const char *ha_state = crm_element_value(node_state, XML_CIB_ATTR_HASTATE);
const char *exp_state = crm_element_value(node_state, XML_CIB_ATTR_EXPSTATE);
if (ha_state == NULL) {
ha_state = DEADSTATUS;
}
if (!crm_is_true(ccm_state) || safe_str_eq(ha_state, DEADSTATUS)) {
crm_trace("Node is down: ha_state=%s, ccm_state=%s", crm_str(ha_state), crm_str(ccm_state));
} else if (safe_str_eq(crm_state, ONLINESTATUS)) {
if (safe_str_eq(join_state, CRMD_JOINSTATE_MEMBER)) {
online = TRUE;
} else {
crm_debug("Node is not ready to run resources: %s", join_state);
}
} else if (this_node->details->expected_up == FALSE) {
crm_trace("CRMd is down: ha_state=%s, ccm_state=%s", crm_str(ha_state), crm_str(ccm_state));
crm_trace("\tcrm_state=%s, join_state=%s, expected=%s",
crm_str(crm_state), crm_str(join_state), crm_str(exp_state));
} else {
/* mark it unclean */
pe_fence_node(data_set, this_node, "because it is partially and/or un-expectedly down");
crm_info("\tha_state=%s, ccm_state=%s,"
" crm_state=%s, join_state=%s, expected=%s",
crm_str(ha_state), crm_str(ccm_state),
crm_str(crm_state), crm_str(join_state), crm_str(exp_state));
}
return online;
}
static gboolean
determine_online_status_fencing(pe_working_set_t * data_set, xmlNode * node_state,
node_t * this_node)
{
gboolean online = FALSE;
gboolean do_terminate = FALSE;
const char *join_state = crm_element_value(node_state, XML_CIB_ATTR_JOINSTATE);
const char *crm_state = crm_element_value(node_state, XML_CIB_ATTR_CRMDSTATE);
const char *ccm_state = crm_element_value(node_state, XML_CIB_ATTR_INCCM);
const char *ha_state = crm_element_value(node_state, XML_CIB_ATTR_HASTATE);
const char *exp_state = crm_element_value(node_state, XML_CIB_ATTR_EXPSTATE);
const char *terminate = g_hash_table_lookup(this_node->details->attrs, "terminate");
if (ha_state == NULL) {
ha_state = DEADSTATUS;
}
if (crm_is_true(terminate)) {
do_terminate = TRUE;
} else if (terminate != NULL && strlen(terminate) > 0) {
/* could be a time() value */
char t = terminate[0];
if (t != '0' && isdigit(t)) {
do_terminate = TRUE;
}
}
if (crm_is_true(ccm_state)
&& safe_str_eq(ha_state, ACTIVESTATUS)
&& safe_str_eq(crm_state, ONLINESTATUS)) {
if (safe_str_eq(join_state, CRMD_JOINSTATE_MEMBER)) {
online = TRUE;
if (do_terminate) {
pe_fence_node(data_set, this_node, "because termination was requested");
}
} else if (join_state == exp_state /* == NULL */ ) {
crm_info("Node %s is coming up", this_node->details->uname);
crm_debug("\tha_state=%s, ccm_state=%s,"
" crm_state=%s, join_state=%s, expected=%s",
crm_str(ha_state), crm_str(ccm_state),
crm_str(crm_state), crm_str(join_state), crm_str(exp_state));
} else if (safe_str_eq(join_state, CRMD_JOINSTATE_PENDING)) {
crm_info("Node %s is not ready to run resources", this_node->details->uname);
this_node->details->standby = TRUE;
this_node->details->pending = TRUE;
online = TRUE;
} else if (safe_str_eq(join_state, CRMD_JOINSTATE_NACK)) {
crm_warn("Node %s is not part of the cluster", this_node->details->uname);
this_node->details->standby = TRUE;
this_node->details->pending = TRUE;
online = TRUE;
} else if (safe_str_eq(join_state, exp_state)) {
crm_info("Node %s is still coming up: %s", this_node->details->uname, join_state);
crm_info("\tha_state=%s, ccm_state=%s, crm_state=%s",
crm_str(ha_state), crm_str(ccm_state), crm_str(crm_state));
this_node->details->standby = TRUE;
this_node->details->pending = TRUE;
online = TRUE;
} else {
crm_warn("Node %s (%s) is un-expectedly down",
this_node->details->uname, this_node->details->id);
crm_info("\tha_state=%s, ccm_state=%s,"
" crm_state=%s, join_state=%s, expected=%s",
crm_str(ha_state), crm_str(ccm_state),
crm_str(crm_state), crm_str(join_state), crm_str(exp_state));
pe_fence_node(data_set, this_node, "because it is un-expectedly down");
}
} else if (crm_is_true(ccm_state) == FALSE && safe_str_eq(ha_state, DEADSTATUS)
&& safe_str_eq(crm_state, OFFLINESTATUS)
&& this_node->details->expected_up == FALSE) {
crm_debug("Node %s is down: join_state=%s, expected=%s",
this_node->details->uname, crm_str(join_state), crm_str(exp_state));
#if 0
/* While a nice optimization, it causes the cluster to block until the node
* comes back online. Which is a serious problem if the cluster software
* is not configured to start at boot or stonith is configured to merely
* stop the node instead of restart it.
* Easily triggered by setting terminate=true for the DC
*/
} else if (do_terminate) {
crm_info("Node %s is %s after forced termination",
this_node->details->uname, crm_is_true(ccm_state) ? "coming up" : "going down");
crm_debug("\tha_state=%s, ccm_state=%s,"
" crm_state=%s, join_state=%s, expected=%s",
crm_str(ha_state), crm_str(ccm_state),
crm_str(crm_state), crm_str(join_state), crm_str(exp_state));
if (crm_is_true(ccm_state) == FALSE) {
this_node->details->standby = TRUE;
this_node->details->pending = TRUE;
online = TRUE;
}
#endif
} else if (this_node->details->expected_up) {
/* mark it unclean */
pe_fence_node(data_set, this_node, "because it is un-expectedly down");
crm_info("\tha_state=%s, ccm_state=%s,"
" crm_state=%s, join_state=%s, expected=%s",
crm_str(ha_state), crm_str(ccm_state),
crm_str(crm_state), crm_str(join_state), crm_str(exp_state));
} else {
crm_info("Node %s is down", this_node->details->uname);
crm_debug("\tha_state=%s, ccm_state=%s,"
" crm_state=%s, join_state=%s, expected=%s",
crm_str(ha_state), crm_str(ccm_state),
crm_str(crm_state), crm_str(join_state), crm_str(exp_state));
}
return online;
}
gboolean
determine_online_status(xmlNode * node_state, node_t * this_node, pe_working_set_t * data_set)
{
gboolean online = FALSE;
const char *shutdown = NULL;
const char *exp_state = crm_element_value(node_state, XML_CIB_ATTR_EXPSTATE);
if (this_node == NULL) {
crm_config_err("No node to check");
return online;
}
this_node->details->shutdown = FALSE;
this_node->details->expected_up = FALSE;
shutdown = g_hash_table_lookup(this_node->details->attrs, XML_CIB_ATTR_SHUTDOWN);
if (shutdown != NULL && safe_str_neq("0", shutdown)) {
this_node->details->shutdown = TRUE;
} else if (safe_str_eq(exp_state, CRMD_JOINSTATE_MEMBER)) {
this_node->details->expected_up = TRUE;
}
if (is_set(data_set->flags, pe_flag_stonith_enabled) == FALSE) {
online = determine_online_status_no_fencing(data_set, node_state, this_node);
} else {
online = determine_online_status_fencing(data_set, node_state, this_node);
}
if (online) {
this_node->details->online = TRUE;
} else {
/* remove node from contention */
this_node->fixed = TRUE;
this_node->weight = -INFINITY;
}
if (online && this_node->details->shutdown) {
/* dont run resources here */
this_node->fixed = TRUE;
this_node->weight = -INFINITY;
}
if (this_node->details->unclean) {
pe_proc_warn("Node %s is unclean", this_node->details->uname);
} else if (this_node->details->online) {
crm_info("Node %s is %s", this_node->details->uname,
this_node->details->shutdown ? "shutting down" :
this_node->details->pending ? "pending" :
this_node->details->standby ? "standby" : "online");
} else {
crm_trace("Node %s is offline", this_node->details->uname);
}
return online;
}
#define set_char(x) last_rsc_id[lpc] = x; complete = TRUE;
char *
clone_zero(const char *last_rsc_id)
{
int lpc = 0;
char *zero = NULL;
CRM_CHECK(last_rsc_id != NULL, return NULL);
if (last_rsc_id != NULL) {
lpc = strlen(last_rsc_id);
}
while (--lpc > 0) {
switch (last_rsc_id[lpc]) {
case 0:
return NULL;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
break;
case ':':
zero = calloc(1, lpc + 3);
memcpy(zero, last_rsc_id, lpc);
zero[lpc] = ':';
zero[lpc + 1] = '0';
zero[lpc + 2] = 0;
return zero;
}
}
return NULL;
}
char *
increment_clone(char *last_rsc_id)
{
int lpc = 0;
int len = 0;
char *tmp = NULL;
gboolean complete = FALSE;
CRM_CHECK(last_rsc_id != NULL, return NULL);
if (last_rsc_id != NULL) {
len = strlen(last_rsc_id);
}
lpc = len - 1;
while (complete == FALSE && lpc > 0) {
switch (last_rsc_id[lpc]) {
case 0:
lpc--;
break;
case '0':
set_char('1');
break;
case '1':
set_char('2');
break;
case '2':
set_char('3');
break;
case '3':
set_char('4');
break;
case '4':
set_char('5');
break;
case '5':
set_char('6');
break;
case '6':
set_char('7');
break;
case '7':
set_char('8');
break;
case '8':
set_char('9');
break;
case '9':
last_rsc_id[lpc] = '0';
lpc--;
break;
case ':':
tmp = last_rsc_id;
last_rsc_id = calloc(1, len + 2);
memcpy(last_rsc_id, tmp, len);
last_rsc_id[++lpc] = '1';
last_rsc_id[len] = '0';
last_rsc_id[len + 1] = 0;
complete = TRUE;
free(tmp);
break;
default:
crm_err("Unexpected char: %c (%d)", last_rsc_id[lpc], lpc);
return NULL;
break;
}
}
return last_rsc_id;
}
static int
get_clone(char *last_rsc_id)
{
int clone = 0;
int lpc = 0;
int len = 0;
CRM_CHECK(last_rsc_id != NULL, return -1);
if (last_rsc_id != NULL) {
len = strlen(last_rsc_id);
}
lpc = len - 1;
while (lpc > 0) {
switch (last_rsc_id[lpc]) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
clone += (int)(last_rsc_id[lpc] - '0') * (len - lpc);
lpc--;
break;
case ':':
return clone;
break;
default:
crm_err("Unexpected char: %d (%c)", lpc, last_rsc_id[lpc]);
return clone;
break;
}
}
return -1;
}
static resource_t *
create_fake_resource(const char *rsc_id, xmlNode * rsc_entry, pe_working_set_t * data_set)
{
resource_t *rsc = NULL;
xmlNode *xml_rsc = create_xml_node(NULL, XML_CIB_TAG_RESOURCE);
copy_in_properties(xml_rsc, rsc_entry);
crm_xml_add(xml_rsc, XML_ATTR_ID, rsc_id);
crm_log_xml_debug(xml_rsc, "Orphan resource");
if (!common_unpack(xml_rsc, &rsc, NULL, data_set)) {
return NULL;
}
set_bit(rsc->flags, pe_rsc_orphan);
data_set->resources = g_list_append(data_set->resources, rsc);
return rsc;
}
extern resource_t *create_child_clone(resource_t * rsc, int sub_id, pe_working_set_t * data_set);
static resource_t *
find_clone(pe_working_set_t * data_set, node_t * node, resource_t * parent, const char *rsc_id)
{
int len = 0;
resource_t *rsc = NULL;
char *base = clone_zero(rsc_id);
char *alt_rsc_id = NULL;
CRM_ASSERT(parent != NULL);
CRM_ASSERT(parent->variant == pe_clone || parent->variant == pe_master);
if (base) {
len = strlen(base);
}
if (len > 0) {
base[len - 1] = 0;
}
crm_trace("Looking for %s on %s in %s %d",
rsc_id, node->details->uname, parent->id, is_set(parent->flags, pe_rsc_unique));
if (is_set(parent->flags, pe_rsc_unique)) {
crm_trace("Looking for %s", rsc_id);
rsc = parent->fns->find_rsc(parent, rsc_id, NULL, pe_find_current);
} else {
crm_trace("Looking for %s on %s", base, node->details->uname);
rsc = parent->fns->find_rsc(parent, base, node, pe_find_partial | pe_find_current);
if (rsc != NULL && rsc->running_on) {
GListPtr gIter = parent->children;
rsc = NULL;
crm_trace("Looking for an existing orphan for %s: %s on %s", parent->id, rsc_id,
node->details->uname);
/* There is already an instance of this _anonymous_ clone active on "node".
*
* If there is a partially active orphan (only applies to clone groups) on
* the same node, use that.
* Otherwise create a new (orphaned) instance at "orphan_check:".
*/
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child = (resource_t *) gIter->data;
node_t *loc = child->fns->location(child, NULL, TRUE);
if (loc && loc->details == node->details) {
resource_t *tmp =
child->fns->find_rsc(child, base, NULL, pe_find_partial | pe_find_current);
if (tmp && tmp->running_on == NULL) {
rsc = tmp;
break;
}
}
}
goto orphan_check;
} else if (((resource_t *) parent->children->data)->variant == pe_group) {
/* If we're grouped, we need to look for a peer thats active on $node
* and use their clone instance number
*/
resource_t *peer =
parent->fns->find_rsc(parent, NULL, node, pe_find_clone | pe_find_current);
if (peer && peer->running_on) {
char buffer[256];
int clone_num = get_clone(peer->id);
snprintf(buffer, 256, "%s%d", base, clone_num);
rsc =
parent->fns->find_rsc(parent, buffer, node, pe_find_current | pe_find_inactive);
if (rsc) {
crm_trace("Found someone active: %s on %s, becoming %s", peer->id,
((node_t *) peer->running_on->data)->details->uname, buffer);
}
}
}
if (parent->fns->find_rsc(parent, rsc_id, NULL, pe_find_current)) {
alt_rsc_id = crm_strdup(rsc_id);
} else {
alt_rsc_id = clone_zero(rsc_id);
}
while (rsc == NULL) {
rsc = parent->fns->find_rsc(parent, alt_rsc_id, NULL, pe_find_current);
if (rsc == NULL) {
crm_trace("Unknown resource: %s", alt_rsc_id);
break;
}
if (rsc->running_on == NULL) {
crm_trace("Resource %s: just right", alt_rsc_id);
break;
}
crm_trace("Resource %s: already active", alt_rsc_id);
alt_rsc_id = increment_clone(alt_rsc_id);
rsc = NULL;
}
}
orphan_check:
if (rsc == NULL) {
/* Create an extra orphan */
resource_t *top = create_child_clone(parent, -1, data_set);
crm_debug("Created orphan for %s: %s on %s", parent->id, rsc_id, node->details->uname);
rsc = top->fns->find_rsc(top, base, NULL, pe_find_current | pe_find_partial);
CRM_ASSERT(rsc != NULL);
}
free(rsc->clone_name);
rsc->clone_name = NULL;
if (safe_str_neq(rsc_id, rsc->id)) {
crm_info("Internally renamed %s on %s to %s%s",
rsc_id, node->details->uname, rsc->id,
is_set(rsc->flags, pe_rsc_orphan) ? " (ORPHAN)" : "");
rsc->clone_name = crm_strdup(rsc_id);
}
free(alt_rsc_id);
free(base);
return rsc;
}
static resource_t *
unpack_find_resource(pe_working_set_t * data_set, node_t * node, const char *rsc_id,
xmlNode * rsc_entry)
{
resource_t *rsc = NULL;
resource_t *clone_parent = NULL;
char *alt_rsc_id = crm_strdup(rsc_id);
crm_trace("looking for %s", rsc_id);
rsc = pe_find_resource(data_set->resources, alt_rsc_id);
/* no match */
if (rsc == NULL) {
/* Even when clone-max=0, we still create a single :0 orphan to match against */
char *tmp = clone_zero(alt_rsc_id);
resource_t *clone0 = pe_find_resource(data_set->resources, tmp);
clone_parent = uber_parent(clone0);
free(tmp);
crm_trace("%s not found: %s", alt_rsc_id, clone_parent ? clone_parent->id : "orphan");
} else {
clone_parent = uber_parent(rsc);
}
if (clone_parent && clone_parent->variant > pe_group) {
rsc = find_clone(data_set, node, clone_parent, rsc_id);
CRM_ASSERT(rsc != NULL);
}
free(alt_rsc_id);
return rsc;
}
static resource_t *
process_orphan_resource(xmlNode * rsc_entry, node_t * node, pe_working_set_t * data_set)
{
resource_t *rsc = NULL;
const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID);
crm_debug("Detected orphan resource %s on %s", rsc_id, node->details->uname);
rsc = create_fake_resource(rsc_id, rsc_entry, data_set);
if (is_set(data_set->flags, pe_flag_stop_rsc_orphans) == FALSE) {
clear_bit(rsc->flags, pe_rsc_managed);
} else {
GListPtr gIter = NULL;
print_resource(LOG_DEBUG_3, "Added orphan", rsc, FALSE);
CRM_CHECK(rsc != NULL, return NULL);
resource_location(rsc, NULL, -INFINITY, "__orphan_dont_run__", data_set);
for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
node_t *node = (node_t *) gIter->data;
if (node->details->online && get_failcount(node, rsc, NULL, data_set)) {
action_t *clear_op = NULL;
action_t *ready = get_pseudo_op(CRM_OP_PROBED, data_set);
clear_op = custom_action(rsc, crm_concat(rsc->id, CRM_OP_CLEAR_FAILCOUNT, '_'),
CRM_OP_CLEAR_FAILCOUNT, node, FALSE, TRUE, data_set);
add_hash_param(clear_op->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE);
crm_info("Clearing failcount (%d) for orphaned resource %s on %s (%s)",
get_failcount(node, rsc, NULL, data_set), rsc->id, node->details->uname,
clear_op->uuid);
order_actions(clear_op, ready, pe_order_optional);
}
}
}
return rsc;
}
static void
process_rsc_state(resource_t * rsc, node_t * node,
enum action_fail_response on_fail,
xmlNode * migrate_op, pe_working_set_t * data_set)
{
crm_trace("Resource %s is %s on %s: on_fail=%s",
rsc->id, role2text(rsc->role), node->details->uname, fail2text(on_fail));
/* process current state */
if (rsc->role != RSC_ROLE_UNKNOWN) {
resource_t *iter = rsc;
while (iter) {
if (g_hash_table_lookup(iter->known_on, node->details->id) == NULL) {
node_t *n = node_copy(node);
g_hash_table_insert(iter->known_on, (gpointer) n->details->id, n);
}
if (is_set(iter->flags, pe_rsc_unique)) {
break;
}
iter = iter->parent;
}
}
if (node->details->unclean) {
/* No extra processing needed
* Also allows resources to be started again after a node is shot
*/
on_fail = action_fail_ignore;
}
switch (on_fail) {
case action_fail_ignore:
/* nothing to do */
break;
case action_fail_fence:
/* treat it as if it is still running
* but also mark the node as unclean
*/
pe_fence_node(data_set, node, "to recover from resource failure(s)");
break;
case action_fail_standby:
node->details->standby = TRUE;
node->details->standby_onfail = TRUE;
break;
case action_fail_block:
/* is_managed == FALSE will prevent any
* actions being sent for the resource
*/
clear_bit(rsc->flags, pe_rsc_managed);
set_bit(rsc->flags, pe_rsc_block);
break;
case action_fail_migrate:
/* make sure it comes up somewhere else
* or not at all
*/
resource_location(rsc, node, -INFINITY, "__action_migration_auto__", data_set);
break;
case action_fail_stop:
rsc->next_role = RSC_ROLE_STOPPED;
break;
case action_fail_recover:
if (rsc->role != RSC_ROLE_STOPPED && rsc->role != RSC_ROLE_UNKNOWN) {
set_bit(rsc->flags, pe_rsc_failed);
stop_action(rsc, node, FALSE);
}
break;
}
if (rsc->role != RSC_ROLE_STOPPED && rsc->role != RSC_ROLE_UNKNOWN) {
if (is_set(rsc->flags, pe_rsc_orphan)) {
if (is_set(rsc->flags, pe_rsc_managed)) {
crm_config_warn("Detected active orphan %s running on %s",
rsc->id, node->details->uname);
} else {
crm_config_warn("Cluster configured not to stop active orphans."
" %s must be stopped manually on %s",
rsc->id, node->details->uname);
}
}
native_add_running(rsc, node, data_set);
if (on_fail != action_fail_ignore) {
set_bit(rsc->flags, pe_rsc_failed);
}
} else if (rsc->clone_name) {
crm_trace("Resetting clone_name %s for %s (stopped)", rsc->clone_name, rsc->id);
free(rsc->clone_name);
rsc->clone_name = NULL;
} else {
char *key = stop_key(rsc);
GListPtr possible_matches = find_actions(rsc->actions, key, node);
GListPtr gIter = possible_matches;
for (; gIter != NULL; gIter = gIter->next) {
action_t *stop = (action_t *) gIter->data;
stop->flags |= pe_action_optional;
}
free(key);
}
}
/* create active recurring operations as optional */
static void
process_recurring(node_t * node, resource_t * rsc,
int start_index, int stop_index,
GListPtr sorted_op_list, pe_working_set_t * data_set)
{
int counter = -1;
const char *task = NULL;
const char *status = NULL;
GListPtr gIter = sorted_op_list;
crm_trace("%s: Start index %d, stop index = %d", rsc->id, start_index, stop_index);
for (; gIter != NULL; gIter = gIter->next) {
xmlNode *rsc_op = (xmlNode *) gIter->data;
int interval = 0;
char *key = NULL;
const char *id = ID(rsc_op);
const char *interval_s = NULL;
counter++;
if (node->details->online == FALSE) {
crm_trace("Skipping %s/%s: node is offline", rsc->id, node->details->uname);
break;
/* Need to check if there's a monitor for role="Stopped" */
} else if (start_index < stop_index && counter <= stop_index) {
crm_trace("Skipping %s/%s: resource is not active", id, node->details->uname);
continue;
} else if (counter < start_index) {
crm_trace("Skipping %s/%s: old %d", id, node->details->uname, counter);
continue;
}
interval_s = crm_element_value(rsc_op, XML_LRM_ATTR_INTERVAL);
interval = crm_parse_int(interval_s, "0");
if (interval == 0) {
crm_trace("Skipping %s/%s: non-recurring", id, node->details->uname);
continue;
}
status = crm_element_value(rsc_op, XML_LRM_ATTR_OPSTATUS);
if (safe_str_eq(status, "-1")) {
crm_trace("Skipping %s/%s: status", id, node->details->uname);
continue;
}
task = crm_element_value(rsc_op, XML_LRM_ATTR_TASK);
/* create the action */
key = generate_op_key(rsc->id, task, interval);
crm_trace("Creating %s/%s", key, node->details->uname);
custom_action(rsc, key, task, node, TRUE, TRUE, data_set);
}
}
void
calculate_active_ops(GListPtr sorted_op_list, int *start_index, int *stop_index)
{
int counter = -1;
const char *task = NULL;
const char *status = NULL;
GListPtr gIter = sorted_op_list;
*stop_index = -1;
*start_index = -1;
for (; gIter != NULL; gIter = gIter->next) {
xmlNode *rsc_op = (xmlNode *) gIter->data;
counter++;
task = crm_element_value(rsc_op, XML_LRM_ATTR_TASK);
status = crm_element_value(rsc_op, XML_LRM_ATTR_OPSTATUS);
if (safe_str_eq(task, CRMD_ACTION_STOP)
&& safe_str_eq(status, "0")) {
*stop_index = counter;
} else if (safe_str_eq(task, CRMD_ACTION_START) || safe_str_eq(task, CRMD_ACTION_MIGRATED)) {
*start_index = counter;
} else if (*start_index <= *stop_index && safe_str_eq(task, CRMD_ACTION_STATUS)) {
const char *rc = crm_element_value(rsc_op, XML_LRM_ATTR_RC);
if (safe_str_eq(rc, "0") || safe_str_eq(rc, "8")) {
*start_index = counter;
}
}
}
}
static void
unpack_lrm_rsc_state(node_t * node, xmlNode * rsc_entry, pe_working_set_t * data_set)
{
GListPtr gIter = NULL;
int stop_index = -1;
int start_index = -1;
enum rsc_role_e req_role = RSC_ROLE_UNKNOWN;
const char *task = NULL;
const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID);
resource_t *rsc = NULL;
GListPtr op_list = NULL;
GListPtr sorted_op_list = NULL;
xmlNode *migrate_op = NULL;
xmlNode *rsc_op = NULL;
enum action_fail_response on_fail = FALSE;
enum rsc_role_e saved_role = RSC_ROLE_UNKNOWN;
crm_trace("[%s] Processing %s on %s",
crm_element_name(rsc_entry), rsc_id, node->details->uname);
/* extract operations */
op_list = NULL;
sorted_op_list = NULL;
for (rsc_op = __xml_first_child(rsc_entry); rsc_op != NULL; rsc_op = __xml_next(rsc_op)) {
if (crm_str_eq((const char *)rsc_op->name, XML_LRM_TAG_RSC_OP, TRUE)) {
op_list = g_list_prepend(op_list, rsc_op);
}
}
if (op_list == NULL) {
/* if there are no operations, there is nothing to do */
return;
}
/* find the resource */
rsc = unpack_find_resource(data_set, node, rsc_id, rsc_entry);
if (rsc == NULL) {
rsc = process_orphan_resource(rsc_entry, node, data_set);
}
CRM_ASSERT(rsc != NULL);
/* process operations */
saved_role = rsc->role;
on_fail = action_fail_ignore;
rsc->role = RSC_ROLE_UNKNOWN;
sorted_op_list = g_list_sort(op_list, sort_op_by_callid);
for (gIter = sorted_op_list; gIter != NULL; gIter = gIter->next) {
xmlNode *rsc_op = (xmlNode *) gIter->data;
task = crm_element_value(rsc_op, XML_LRM_ATTR_TASK);
if (safe_str_eq(task, CRMD_ACTION_MIGRATED)) {
migrate_op = rsc_op;
}
unpack_rsc_op(rsc, node, rsc_op, gIter->next, &on_fail, data_set);
}
/* create active recurring operations as optional */
calculate_active_ops(sorted_op_list, &start_index, &stop_index);
process_recurring(node, rsc, start_index, stop_index, sorted_op_list, data_set);
/* no need to free the contents */
g_list_free(sorted_op_list);
process_rsc_state(rsc, node, on_fail, migrate_op, data_set);
if (get_target_role(rsc, &req_role)) {
if (rsc->next_role == RSC_ROLE_UNKNOWN || req_role < rsc->next_role) {
crm_debug("%s: Overwriting calculated next role %s"
" with requested next role %s",
rsc->id, role2text(rsc->next_role), role2text(req_role));
rsc->next_role = req_role;
} else if (req_role > rsc->next_role) {
crm_info("%s: Not overwriting calculated next role %s"
" with requested next role %s",
rsc->id, role2text(rsc->next_role), role2text(req_role));
}
}
if (saved_role > rsc->role) {
rsc->role = saved_role;
}
}
gboolean
unpack_lrm_resources(node_t * node, xmlNode * lrm_rsc_list, pe_working_set_t * data_set)
{
xmlNode *rsc_entry = NULL;
CRM_CHECK(node != NULL, return FALSE);
crm_trace("Unpacking resources on %s", node->details->uname);
for (rsc_entry = __xml_first_child(lrm_rsc_list); rsc_entry != NULL;
rsc_entry = __xml_next(rsc_entry)) {
if (crm_str_eq((const char *)rsc_entry->name, XML_LRM_TAG_RESOURCE, TRUE)) {
unpack_lrm_rsc_state(node, rsc_entry, data_set);
}
}
return TRUE;
}
static void
set_active(resource_t * rsc)
{
resource_t *top = uber_parent(rsc);
if (top && top->variant == pe_master) {
rsc->role = RSC_ROLE_SLAVE;
} else {
rsc->role = RSC_ROLE_STARTED;
}
}
static void
set_node_score(gpointer key, gpointer value, gpointer user_data)
{
node_t *node = value;
int *score = user_data;
node->weight = *score;
}
#define STATUS_PATH_MAX 1024
static xmlNode *
find_lrm_op(const char *resource, const char *op, const char *node, const char *source,
pe_working_set_t * data_set)
{
int offset = 0;
char xpath[STATUS_PATH_MAX];
offset += snprintf(xpath + offset, STATUS_PATH_MAX - offset, "//node_state[@uname='%s']", node);
offset +=
snprintf(xpath + offset, STATUS_PATH_MAX - offset, "//" XML_LRM_TAG_RESOURCE "[@id='%s']",
resource);
/* Need to check against transition_magic too? */
if (source && safe_str_eq(op, CRMD_ACTION_MIGRATE)) {
offset +=
snprintf(xpath + offset, STATUS_PATH_MAX - offset,
"/" XML_LRM_TAG_RSC_OP "[@operation='%s' and @migrate_target='%s']", op,
source);
} else if (source && safe_str_eq(op, CRMD_ACTION_MIGRATED)) {
offset +=
snprintf(xpath + offset, STATUS_PATH_MAX - offset,
"/" XML_LRM_TAG_RSC_OP "[@operation='%s' and @migrate_source='%s']", op,
source);
} else {
offset +=
snprintf(xpath + offset, STATUS_PATH_MAX - offset,
"/" XML_LRM_TAG_RSC_OP "[@operation='%s']", op);
}
return get_xpath_object(xpath, data_set->input, LOG_DEBUG);
}
gboolean
unpack_rsc_op(resource_t * rsc, node_t * node, xmlNode * xml_op, GListPtr next,
enum action_fail_response * on_fail, pe_working_set_t * data_set)
{
int task_id = 0;
const char *id = NULL;
const char *key = NULL;
const char *task = NULL;
const char *task_key = NULL;
const char *magic = NULL;
const char *actual_rc = NULL;
/* const char *target_rc = NULL; */
const char *task_status = NULL;
const char *interval_s = NULL;
const char *op_version = NULL;
int interval = 0;
int task_status_i = -2;
int actual_rc_i = 0;
int target_rc = -1;
int last_failure = 0;
action_t *action = NULL;
node_t *effective_node = NULL;
resource_t *failed = NULL;
gboolean expired = FALSE;
gboolean is_probe = FALSE;
gboolean clear_past_failure = FALSE;
CRM_CHECK(rsc != NULL, return FALSE);
CRM_CHECK(node != NULL, return FALSE);
CRM_CHECK(xml_op != NULL, return FALSE);
id = ID(xml_op);
task = crm_element_value(xml_op, XML_LRM_ATTR_TASK);
task_key = crm_element_value(xml_op, XML_LRM_ATTR_TASK_KEY);
task_status = crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS);
op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION);
magic = crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC);
key = crm_element_value(xml_op, XML_ATTR_TRANSITION_KEY);
crm_element_value_int(xml_op, XML_LRM_ATTR_CALLID, &task_id);
CRM_CHECK(id != NULL, return FALSE);
CRM_CHECK(task != NULL, return FALSE);
CRM_CHECK(task_status != NULL, return FALSE);
task_status_i = crm_parse_int(task_status, NULL);
CRM_CHECK(task_status_i <= PCMK_LRM_OP_ERROR, return FALSE);
CRM_CHECK(task_status_i >= PCMK_LRM_OP_PENDING, return FALSE);
if (safe_str_eq(task, CRMD_ACTION_NOTIFY)) {
/* safe to ignore these */
return TRUE;
}
if (rsc->failure_timeout > 0) {
int last_run = 0;
if (crm_element_value_int(xml_op, "last-rc-change", &last_run) == 0) {
time_t now = get_timet_now(data_set);
if (now > (last_run + rsc->failure_timeout)) {
expired = TRUE;
}
}
}
crm_trace("Unpacking task %s/%s (call_id=%d, status=%s) on %s (role=%s)",
id, task, task_id, task_status, node->details->uname, role2text(rsc->role));
interval_s = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL);
interval = crm_parse_int(interval_s, "0");
if (interval == 0 && safe_str_eq(task, CRMD_ACTION_STATUS)) {
is_probe = TRUE;
}
if (node->details->unclean) {
crm_trace("Node %s (where %s is running) is unclean."
" Further action depends on the value of the stop's on-fail attribue",
node->details->uname, rsc->id);
}
actual_rc = crm_element_value(xml_op, XML_LRM_ATTR_RC);
CRM_CHECK(actual_rc != NULL, return FALSE);
actual_rc_i = crm_parse_int(actual_rc, NULL);
if (key) {
int dummy = 0;
char *dummy_string = NULL;
decode_transition_key(key, &dummy_string, &dummy, &dummy, &target_rc);
free(dummy_string);
}
if (task_status_i == PCMK_LRM_OP_DONE && target_rc >= 0) {
if (target_rc == actual_rc_i) {
task_status_i = PCMK_LRM_OP_DONE;
} else {
task_status_i = PCMK_LRM_OP_ERROR;
crm_debug("%s on %s returned %d (%s) instead of the expected value: %d (%s)",
id, node->details->uname,
actual_rc_i, lrmd_event_rc2str(actual_rc_i),
target_rc, lrmd_event_rc2str(target_rc));
}
} else if (task_status_i == PCMK_LRM_OP_ERROR) {
/* let us decide that */
task_status_i = PCMK_LRM_OP_DONE;
}
if (task_status_i == PCMK_LRM_OP_NOTSUPPORTED) {
actual_rc_i = PCMK_EXECRA_UNIMPLEMENT_FEATURE;
}
if (task_status_i != actual_rc_i
&& rsc->failure_timeout > 0 && get_failcount(node, rsc, &last_failure, data_set) == 0) {
if (last_failure > 0) {
action_t *clear_op = NULL;
clear_op = custom_action(rsc, crm_concat(rsc->id, CRM_OP_CLEAR_FAILCOUNT, '_'),
CRM_OP_CLEAR_FAILCOUNT, node, FALSE, TRUE, data_set);
add_hash_param(clear_op->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE);
crm_notice("Clearing expired failcount for %s on %s", rsc->id, node->details->uname);
}
}
if (expired
&& actual_rc_i != PCMK_EXECRA_NOT_RUNNING
&& actual_rc_i != PCMK_EXECRA_RUNNING_MASTER && actual_rc_i != PCMK_EXECRA_OK) {
crm_notice("Ignoring expired failure %s (rc=%d, magic=%s) on %s",
id, actual_rc_i, magic, node->details->uname);
goto done;
}
/* we could clean this up significantly except for old LRMs and CRMs that
* didnt include target_rc and liked to remap status
*/
switch (actual_rc_i) {
case PCMK_EXECRA_NOT_RUNNING:
if (is_probe || target_rc == actual_rc_i) {
task_status_i = PCMK_LRM_OP_DONE;
rsc->role = RSC_ROLE_STOPPED;
/* clear any previous failure actions */
*on_fail = action_fail_ignore;
rsc->next_role = RSC_ROLE_UNKNOWN;
} else if (safe_str_neq(task, CRMD_ACTION_STOP)) {
task_status_i = PCMK_LRM_OP_ERROR;
}
break;
case PCMK_EXECRA_RUNNING_MASTER:
if (is_probe) {
task_status_i = PCMK_LRM_OP_DONE;
crm_notice("Operation %s found resource %s active in master mode on %s",
task, rsc->id, node->details->uname);
} else if (target_rc == actual_rc_i) {
/* nothing to do */
} else if (target_rc >= 0) {
task_status_i = PCMK_LRM_OP_ERROR;
/* legacy code for pre-0.6.5 operations */
} else if (safe_str_neq(task, CRMD_ACTION_STATUS)
|| rsc->role != RSC_ROLE_MASTER) {
task_status_i = PCMK_LRM_OP_ERROR;
if (rsc->role != RSC_ROLE_MASTER) {
crm_err("%s reported %s in master mode on %s",
id, rsc->id, node->details->uname);
}
}
rsc->role = RSC_ROLE_MASTER;
break;
case PCMK_EXECRA_FAILED_MASTER:
rsc->role = RSC_ROLE_MASTER;
task_status_i = PCMK_LRM_OP_ERROR;
break;
case PCMK_EXECRA_UNIMPLEMENT_FEATURE:
if (interval > 0) {
task_status_i = PCMK_LRM_OP_NOTSUPPORTED;
break;
}
/* else: fall through */
case PCMK_EXECRA_INSUFFICIENT_PRIV:
case PCMK_EXECRA_NOT_INSTALLED:
case PCMK_EXECRA_INVALID_PARAM:
effective_node = node;
/* fall through */
case PCMK_EXECRA_NOT_CONFIGURED:
failed = rsc;
if (is_not_set(rsc->flags, pe_rsc_unique)) {
failed = uber_parent(failed);
}
do_crm_log(actual_rc_i == PCMK_EXECRA_NOT_INSTALLED ? LOG_NOTICE : LOG_ERR,
"Preventing %s from re-starting %s %s: operation %s failed '%s' (rc=%d)",
failed->id,
effective_node ? "on" : "anywhere in the cluster",
effective_node ? effective_node->details->uname : "",
task, lrmd_event_rc2str(actual_rc_i), actual_rc_i);
resource_location(failed, effective_node, -INFINITY, "hard-error", data_set);
if (is_probe) {
/* treat these like stops */
task = CRMD_ACTION_STOP;
task_status_i = PCMK_LRM_OP_DONE;
crm_xml_add(xml_op, XML_ATTR_UNAME, node->details->uname);
if (actual_rc_i != PCMK_EXECRA_NOT_INSTALLED
|| is_set(data_set->flags, pe_flag_symmetric_cluster)) {
if ((node->details->shutdown == FALSE) || (node->details->online == TRUE)) {
add_node_copy(data_set->failed, xml_op);
}
}
}
break;
case PCMK_EXECRA_OK:
if (is_probe && target_rc == 7) {
task_status_i = PCMK_LRM_OP_DONE;
crm_info("Operation %s found resource %s active on %s",
task, rsc->id, node->details->uname);
/* legacy code for pre-0.6.5 operations */
} else if (target_rc < 0 && interval > 0 && rsc->role == RSC_ROLE_MASTER) {
/* catch status ops that return 0 instead of 8 while they
* are supposed to be in master mode
*/
task_status_i = PCMK_LRM_OP_ERROR;
}
break;
default:
if (task_status_i == PCMK_LRM_OP_DONE) {
crm_info("Remapping %s (rc=%d) on %s to an ERROR",
id, actual_rc_i, node->details->uname);
task_status_i = PCMK_LRM_OP_ERROR;
}
}
if (task_status_i == PCMK_LRM_OP_ERROR
|| task_status_i == PCMK_LRM_OP_TIMEOUT || task_status_i == PCMK_LRM_OP_NOTSUPPORTED) {
const char *action_key = task_key ? task_key : id;
action = custom_action(rsc, crm_strdup(action_key), task, NULL, TRUE, FALSE, data_set);
if (expired) {
crm_notice("Ignoring expired failure (calculated) %s (rc=%d, magic=%s) on %s",
id, actual_rc_i, magic, node->details->uname);
goto done;
} else if (action->on_fail == action_fail_ignore) {
crm_warn("Remapping %s (rc=%d) on %s to DONE: ignore",
id, actual_rc_i, node->details->uname);
task_status_i = PCMK_LRM_OP_DONE;
set_bit(rsc->flags, pe_rsc_failure_ignored);
crm_xml_add(xml_op, XML_ATTR_UNAME, node->details->uname);
if ((node->details->shutdown == FALSE) || (node->details->online == TRUE)) {
add_node_copy(data_set->failed, xml_op);
}
}
}
switch (task_status_i) {
case PCMK_LRM_OP_PENDING:
if (safe_str_eq(task, CRMD_ACTION_START)) {
set_bit(rsc->flags, pe_rsc_start_pending);
set_active(rsc);
} else if (safe_str_eq(task, CRMD_ACTION_PROMOTE)) {
rsc->role = RSC_ROLE_MASTER;
}
/*
* Intentionally ignoring pending migrate ops here;
* haven't decided if we need to do anything special
* with them yet...
*/
break;
case PCMK_LRM_OP_DONE:
crm_trace("%s/%s completed on %s", rsc->id, task, node->details->uname);
if (actual_rc_i == PCMK_EXECRA_NOT_RUNNING) {
clear_past_failure = TRUE;
} else if (safe_str_eq(task, CRMD_ACTION_START)) {
rsc->role = RSC_ROLE_STARTED;
clear_past_failure = TRUE;
} else if (safe_str_eq(task, CRMD_ACTION_STOP)) {
rsc->role = RSC_ROLE_STOPPED;
clear_past_failure = TRUE;
} else if (safe_str_eq(task, CRMD_ACTION_PROMOTE)) {
rsc->role = RSC_ROLE_MASTER;
clear_past_failure = TRUE;
} else if (safe_str_eq(task, CRMD_ACTION_DEMOTE)) {
/* Demote from Master does not clear an error */
rsc->role = RSC_ROLE_SLAVE;
} else if (safe_str_eq(task, CRMD_ACTION_MIGRATED)) {
rsc->role = RSC_ROLE_STARTED;
clear_past_failure = TRUE;
} else if (safe_str_eq(task, CRMD_ACTION_MIGRATE)) {
/*
* The normal sequence is (now): migrate_to(Src) -> migrate_from(Tgt) -> stop(Src)
*
* So if a migrate_to is followed by a stop, then we dont need to care what
* happended on the target node
*
* Without the stop, we need to look for a successful migrate_from.
* This would also imply we're no longer running on the source
*
* Without the stop, and without a migrate_from op we make sure the resource
* gets stopped on both source and target (assuming the target is up)
*
*/
int stop_id = 0;
xmlNode *stop_op =
find_lrm_op(rsc->id, CRMD_ACTION_STOP, node->details->id, NULL, data_set);
if (stop_op) {
crm_element_value_int(stop_op, XML_LRM_ATTR_CALLID, &stop_id);
}
if (stop_op == NULL || stop_id < task_id) {
int from_rc = 0, from_status = 0;
const char *migrate_source =
crm_element_value(xml_op, XML_LRM_ATTR_MIGRATE_SOURCE);
const char *migrate_target =
crm_element_value(xml_op, XML_LRM_ATTR_MIGRATE_TARGET);
node_t *target = pe_find_node(data_set->nodes, migrate_target);
node_t *source = pe_find_node(data_set->nodes, migrate_source);
xmlNode *migrate_from =
find_lrm_op(rsc->id, CRMD_ACTION_MIGRATED, migrate_target, migrate_source,
data_set);
rsc->role = RSC_ROLE_STARTED; /* can be master? */
if (migrate_from) {
crm_element_value_int(migrate_from, XML_LRM_ATTR_RC, &from_rc);
crm_element_value_int(migrate_from, XML_LRM_ATTR_OPSTATUS, &from_status);
crm_trace("%s op on %s exited with status=%d, rc=%d",
ID(migrate_from), migrate_target, from_status, from_rc);
}
if (migrate_from && from_rc == PCMK_EXECRA_OK && from_status == PCMK_LRM_OP_DONE) {
crm_trace("Detected dangling migration op: %s on %s", ID(xml_op),
migrate_source);
/* all good
* just need to arrange for the stop action to get sent
* but _without_ affecting the target somehow
*/
rsc->role = RSC_ROLE_STOPPED;
rsc->dangling_migrations = g_list_prepend(rsc->dangling_migrations, node);
} else if (migrate_from) { /* Failed */
crm_trace("Marking active on %s %p %d", migrate_target, target,
target->details->online);
if (target && target->details->online) {
native_add_running(rsc, target, data_set);
}
} else { /* Pending or complete but erased */
node_t *target = pe_find_node_id(data_set->nodes, migrate_target);
crm_trace("Marking active on %s %p %d", migrate_target, target,
target->details->online);
if (target && target->details->online) {
native_add_running(rsc, target, data_set);
if (source && source->details->online) {
/* If we make it here we have a partial migration. The migrate_to
* has completed but the migrate_from on the target has not. Hold on
* to the target and source on the resource. Later on if we detect that
* the resource is still going to run on that target, we may continue
* the migration */
rsc->partial_migration_target = target;
rsc->partial_migration_source = source;
}
} else {
/* Consider it failed here - forces a restart, prevents migration */
set_bit_inplace(rsc->flags, pe_rsc_failed);
}
}
}
} else if (rsc->role < RSC_ROLE_STARTED) {
/* start, migrate_to and migrate_from will land here */
crm_trace("%s active on %s", rsc->id, node->details->uname);
set_active(rsc);
}
/* clear any previous failure actions */
if (clear_past_failure) {
switch (*on_fail) {
case action_fail_block:
case action_fail_stop:
case action_fail_fence:
case action_fail_migrate:
case action_fail_standby:
crm_trace("%s.%s is not cleared by a completed stop",
rsc->id, fail2text(*on_fail));
break;
case action_fail_ignore:
case action_fail_recover:
*on_fail = action_fail_ignore;
rsc->next_role = RSC_ROLE_UNKNOWN;
}
}
break;
case PCMK_LRM_OP_ERROR:
case PCMK_LRM_OP_TIMEOUT:
case PCMK_LRM_OP_NOTSUPPORTED:
crm_warn("Processing failed op %s on %s: %s (%d)",
id, node->details->uname, lrmd_event_rc2str(actual_rc_i), actual_rc_i);
crm_xml_add(xml_op, XML_ATTR_UNAME, node->details->uname);
if ((node->details->shutdown == FALSE) || (node->details->online == TRUE)) {
add_node_copy(data_set->failed, xml_op);
}
if (*on_fail < action->on_fail) {
*on_fail = action->on_fail;
}
if (safe_str_eq(task, CRMD_ACTION_STOP)) {
resource_location(rsc, node, -INFINITY, "__stop_fail__", data_set);
} else if (safe_str_eq(task, CRMD_ACTION_MIGRATED)) {
int stop_id = 0;
int migrate_id = 0;
const char *migrate_source = crm_element_value(xml_op, XML_LRM_ATTR_MIGRATE_SOURCE);
const char *migrate_target = crm_element_value(xml_op, XML_LRM_ATTR_MIGRATE_TARGET);
xmlNode *stop_op =
find_lrm_op(rsc->id, CRMD_ACTION_STOP, migrate_source, NULL, data_set);
xmlNode *migrate_op =
find_lrm_op(rsc->id, CRMD_ACTION_MIGRATE, migrate_source, migrate_target,
data_set);
if (stop_op) {
crm_element_value_int(stop_op, XML_LRM_ATTR_CALLID, &stop_id);
}
if (migrate_op) {
crm_element_value_int(migrate_op, XML_LRM_ATTR_CALLID, &migrate_id);
}
/* Get our state right */
rsc->role = RSC_ROLE_STARTED; /* can be master? */
if (stop_op == NULL || stop_id < migrate_id) {
node_t *source = pe_find_node(data_set->nodes, migrate_source);
if (source && source->details->online) {
native_add_running(rsc, source, data_set);
}
}
} else if (safe_str_eq(task, CRMD_ACTION_MIGRATE)) {
int stop_id = 0;
int migrate_id = 0;
const char *migrate_source = crm_element_value(xml_op, XML_LRM_ATTR_MIGRATE_SOURCE);
const char *migrate_target = crm_element_value(xml_op, XML_LRM_ATTR_MIGRATE_TARGET);
xmlNode *stop_op =
find_lrm_op(rsc->id, CRMD_ACTION_STOP, migrate_target, NULL, data_set);
xmlNode *migrate_op =
find_lrm_op(rsc->id, CRMD_ACTION_MIGRATED, migrate_target, migrate_source,
data_set);
if (stop_op) {
crm_element_value_int(stop_op, XML_LRM_ATTR_CALLID, &stop_id);
}
if (migrate_op) {
crm_element_value_int(migrate_op, XML_LRM_ATTR_CALLID, &migrate_id);
}
/* Get our state right */
rsc->role = RSC_ROLE_STARTED; /* can be master? */
if (stop_op == NULL || stop_id < migrate_id) {
node_t *target = pe_find_node(data_set->nodes, migrate_target);
crm_trace("Stop: %p %d, Migrated: %p %d", stop_op, stop_id, migrate_op,
migrate_id);
if (target && target->details->online) {
native_add_running(rsc, target, data_set);
}
} else if (migrate_op == NULL) {
/* Make sure it gets cleaned up, the stop may pre-date the migrate_from */
rsc->dangling_migrations = g_list_prepend(rsc->dangling_migrations, node);
}
} else if (safe_str_eq(task, CRMD_ACTION_PROMOTE)) {
rsc->role = RSC_ROLE_MASTER;
} else if (safe_str_eq(task, CRMD_ACTION_DEMOTE)) {
/*
* staying in role=master ends up putting the PE/TE into a loop
* setting role=slave is not dangerous because no master will be
* promoted until the failed resource has been fully stopped
*/
crm_warn("Forcing %s to stop after a failed demote action", rsc->id);
rsc->next_role = RSC_ROLE_STOPPED;
rsc->role = RSC_ROLE_SLAVE;
} else if (compare_version("2.0", op_version) > 0
&& safe_str_eq(task, CRMD_ACTION_START)) {
crm_warn("Compatibility handling for failed op %s on %s", id, node->details->uname);
resource_location(rsc, node, -INFINITY, "__legacy_start__", data_set);
}
if (rsc->role < RSC_ROLE_STARTED) {
set_active(rsc);
}
crm_trace("Resource %s: role=%s, unclean=%s, on_fail=%s, fail_role=%s",
rsc->id, role2text(rsc->role),
node->details->unclean ? "true" : "false",
fail2text(action->on_fail), role2text(action->fail_role));
if (action->fail_role != RSC_ROLE_STARTED && rsc->next_role < action->fail_role) {
rsc->next_role = action->fail_role;
}
if (action->fail_role == RSC_ROLE_STOPPED) {
int score = -INFINITY;
crm_err("Making sure %s doesn't come up again", rsc->id);
/* make sure it doesnt come up again */
g_hash_table_destroy(rsc->allowed_nodes);
rsc->allowed_nodes = node_hash_from_list(data_set->nodes);
g_hash_table_foreach(rsc->allowed_nodes, set_node_score, &score);
}
pe_free_action(action);
action = NULL;
break;
case PCMK_LRM_OP_CANCELLED:
/* do nothing?? */
pe_err("Dont know what to do for cancelled ops yet");
break;
}
done:
crm_trace("Resource %s after %s: role=%s", rsc->id, task, role2text(rsc->role));
pe_free_action(action);
return TRUE;
}
gboolean
add_node_attrs(xmlNode * xml_obj, node_t * node, gboolean overwrite, pe_working_set_t * data_set)
{
g_hash_table_insert(node->details->attrs,
crm_strdup("#" XML_ATTR_UNAME), crm_strdup(node->details->uname));
g_hash_table_insert(node->details->attrs,
crm_strdup("#" XML_ATTR_ID), crm_strdup(node->details->id));
if (safe_str_eq(node->details->id, data_set->dc_uuid)) {
data_set->dc_node = node;
node->details->is_dc = TRUE;
g_hash_table_insert(node->details->attrs,
crm_strdup("#" XML_ATTR_DC), crm_strdup(XML_BOOLEAN_TRUE));
} else {
g_hash_table_insert(node->details->attrs,
crm_strdup("#" XML_ATTR_DC), crm_strdup(XML_BOOLEAN_FALSE));
}
unpack_instance_attributes(data_set->input, xml_obj, XML_TAG_ATTR_SETS, NULL,
node->details->attrs, NULL, overwrite, data_set->now);
return TRUE;
}
static GListPtr
extract_operations(const char *node, const char *rsc, xmlNode * rsc_entry, gboolean active_filter)
{
int counter = -1;
int stop_index = -1;
int start_index = -1;
xmlNode *rsc_op = NULL;
GListPtr gIter = NULL;
GListPtr op_list = NULL;
GListPtr sorted_op_list = NULL;
/* extract operations */
op_list = NULL;
sorted_op_list = NULL;
for (rsc_op = __xml_first_child(rsc_entry); rsc_op != NULL; rsc_op = __xml_next(rsc_op)) {
if (crm_str_eq((const char *)rsc_op->name, XML_LRM_TAG_RSC_OP, TRUE)) {
crm_xml_add(rsc_op, "resource", rsc);
crm_xml_add(rsc_op, XML_ATTR_UNAME, node);
op_list = g_list_prepend(op_list, rsc_op);
}
}
if (op_list == NULL) {
/* if there are no operations, there is nothing to do */
return NULL;
}
sorted_op_list = g_list_sort(op_list, sort_op_by_callid);
/* create active recurring operations as optional */
if (active_filter == FALSE) {
return sorted_op_list;
}
op_list = NULL;
calculate_active_ops(sorted_op_list, &start_index, &stop_index);
for (gIter = sorted_op_list; gIter != NULL; gIter = gIter->next) {
xmlNode *rsc_op = (xmlNode *) gIter->data;
counter++;
if (start_index < stop_index) {
crm_trace("Skipping %s: not active", ID(rsc_entry));
break;
} else if (counter < start_index) {
crm_trace("Skipping %s: old", ID(rsc_op));
continue;
}
op_list = g_list_append(op_list, rsc_op);
}
g_list_free(sorted_op_list);
return op_list;
}
GListPtr
find_operations(const char *rsc, const char *node, gboolean active_filter,
pe_working_set_t * data_set)
{
GListPtr output = NULL;
GListPtr intermediate = NULL;
xmlNode *tmp = NULL;
xmlNode *status = find_xml_node(data_set->input, XML_CIB_TAG_STATUS, TRUE);
const char *uname = NULL;
node_t *this_node = NULL;
xmlNode *node_state = NULL;
for (node_state = __xml_first_child(status); node_state != NULL;
node_state = __xml_next(node_state)) {
if (crm_str_eq((const char *)node_state->name, XML_CIB_TAG_STATE, TRUE)) {
uname = crm_element_value(node_state, XML_ATTR_UNAME);
if (node != NULL && safe_str_neq(uname, node)) {
continue;
}
this_node = pe_find_node(data_set->nodes, uname);
CRM_CHECK(this_node != NULL, continue);
determine_online_status(node_state, this_node, data_set);
if (this_node->details->online || is_set(data_set->flags, pe_flag_stonith_enabled)) {
/* offline nodes run no resources...
* unless stonith is enabled in which case we need to
* make sure rsc start events happen after the stonith
*/
xmlNode *lrm_rsc = NULL;
tmp = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE);
tmp = find_xml_node(tmp, XML_LRM_TAG_RESOURCES, FALSE);
for (lrm_rsc = __xml_first_child(tmp); lrm_rsc != NULL;
lrm_rsc = __xml_next(lrm_rsc)) {
if (crm_str_eq((const char *)lrm_rsc->name, XML_LRM_TAG_RESOURCE, TRUE)) {
const char *rsc_id = crm_element_value(lrm_rsc, XML_ATTR_ID);
if (rsc != NULL && safe_str_neq(rsc_id, rsc)) {
continue;
}
intermediate = extract_operations(uname, rsc_id, lrm_rsc, active_filter);
output = g_list_concat(output, intermediate);
}
}
}
}
}
return output;
}
diff --git a/lib/pengine/utils.c b/lib/pengine/utils.c
index 7c691cf8a2..c3b29d4dfa 100644
--- a/lib/pengine/utils.c
+++ b/lib/pengine/utils.c
@@ -1,1512 +1,1512 @@
/*
* 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/common/util.h>
#include <glib.h>
#include <crm/pengine/rules.h>
#include <utils.h>
pe_working_set_t *pe_dataset = NULL;
extern xmlNode *get_object_root(const char *object_type, xmlNode * the_root);
void print_str_str(gpointer key, gpointer value, gpointer user_data);
gboolean ghash_free_str_str(gpointer key, gpointer value, gpointer user_data);
void unpack_operation(action_t * action, xmlNode * xml_obj, pe_working_set_t * data_set);
static xmlNode *find_rsc_op_entry_helper(resource_t * rsc, const char *key, gboolean include_disabled);
node_t *
node_copy(node_t * this_node)
{
node_t *new_node = NULL;
CRM_CHECK(this_node != NULL, return NULL);
new_node = calloc(1, sizeof(node_t));
CRM_ASSERT(new_node != NULL);
crm_trace("Copying %p (%s) to %p", this_node, this_node->details->uname, new_node);
new_node->weight = this_node->weight;
new_node->fixed = this_node->fixed;
new_node->details = this_node->details;
return new_node;
}
/* any node in list1 or list2 and not in the other gets a score of -INFINITY */
void
node_list_exclude(GHashTable * hash, GListPtr list, gboolean merge_scores)
{
GHashTable *result = hash;
node_t *other_node = NULL;
GListPtr gIter = list;
GHashTableIter iter;
node_t *node = NULL;
g_hash_table_iter_init(&iter, hash);
while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
other_node = pe_find_node_id(list, node->details->id);
if (other_node == NULL) {
node->weight = -INFINITY;
} else if (merge_scores) {
node->weight = merge_weights(node->weight, other_node->weight);
}
}
for (; gIter != NULL; gIter = gIter->next) {
node_t *node = (node_t *) gIter->data;
other_node = pe_hash_table_lookup(result, node->details->id);
if (other_node == NULL) {
node_t *new_node = node_copy(node);
new_node->weight = -INFINITY;
g_hash_table_insert(result, (gpointer) new_node->details->id, new_node);
}
}
}
GHashTable *
node_hash_from_list(GListPtr list)
{
GListPtr gIter = list;
GHashTable *result = g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, g_hash_destroy_str);
for (; gIter != NULL; gIter = gIter->next) {
node_t *node = (node_t *) gIter->data;
node_t *n = node_copy(node);
g_hash_table_insert(result, (gpointer) n->details->id, n);
}
return result;
}
GListPtr
node_list_dup(GListPtr list1, gboolean reset, gboolean filter)
{
GListPtr result = NULL;
GListPtr gIter = list1;
for (; gIter != NULL; gIter = gIter->next) {
node_t *new_node = NULL;
node_t *this_node = (node_t *) gIter->data;
if (filter && this_node->weight < 0) {
continue;
}
new_node = node_copy(this_node);
if (reset) {
new_node->weight = 0;
}
if (new_node != NULL) {
result = g_list_prepend(result, new_node);
}
}
return result;
}
static gint
sort_node_uname(gconstpointer a, gconstpointer b)
{
const node_t *node_a = a;
const node_t *node_b = b;
return strcmp(node_a->details->uname, node_b->details->uname);
}
void
dump_node_scores_worker(int level, const char *file, const char *function, int line,
resource_t * rsc, const char *comment, GHashTable * nodes)
{
GHashTable *hash = nodes;
GHashTableIter iter;
node_t *node = NULL;
if (rsc) {
hash = rsc->allowed_nodes;
}
if (rsc && is_set(rsc->flags, pe_rsc_orphan)) {
/* Don't show the allocation scores for orphans */
return;
}
if (level == 0) {
/* For now we want this in sorted order to keep the regression tests happy */
GListPtr gIter = NULL;
GListPtr list = g_hash_table_get_values(hash);
list = g_list_sort(list, sort_node_uname);
gIter = list;
for (; gIter != NULL; gIter = gIter->next) {
node_t *node = (node_t *) gIter->data;
char *score = score2char(node->weight);
if (rsc) {
printf("%s: %s allocation score on %s: %s\n",
comment, rsc->id, node->details->uname, score);
} else {
printf("%s: %s = %s\n", comment, node->details->uname, score);
}
free(score);
}
g_list_free(list);
} else if (hash) {
g_hash_table_iter_init(&iter, hash);
while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
char *score = score2char(node->weight);
if (rsc) {
do_crm_log_alias(LOG_TRACE, file, function, line,
"%s: %s allocation score on %s: %s", comment, rsc->id,
node->details->uname, score);
} else {
do_crm_log_alias(LOG_TRACE, file, function, line + 1, "%s: %s = %s", comment,
node->details->uname, score);
}
free(score);
}
}
if (rsc && rsc->children) {
GListPtr gIter = NULL;
gIter = rsc->children;
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child = (resource_t *) gIter->data;
dump_node_scores_worker(level, file, function, line, child, comment, nodes);
}
}
}
static void
append_dump_text(gpointer key, gpointer value, gpointer user_data)
{
char **dump_text = user_data;
int len = 0;
char *new_text = NULL;
len = strlen(*dump_text) + strlen(" ") + strlen(key) + strlen("=") + strlen(value) + 1;
new_text = calloc(1, len);
sprintf(new_text, "%s %s=%s", *dump_text, (char *)key, (char *)value);
free(*dump_text);
*dump_text = new_text;
}
void
dump_node_capacity(int level, const char *comment, node_t * node)
{
int len = 0;
char *dump_text = NULL;
len = strlen(comment) + strlen(": ") + strlen(node->details->uname) + strlen(" capacity:") + 1;
dump_text = calloc(1, len);
sprintf(dump_text, "%s: %s capacity:", comment, node->details->uname);
g_hash_table_foreach(node->details->utilization, append_dump_text, &dump_text);
if (level == 0) {
fprintf(stdout, "%s\n", dump_text);
} else {
crm_trace("%s", dump_text);
}
free(dump_text);
}
void
dump_rsc_utilization(int level, const char *comment, resource_t * rsc, node_t * node)
{
int len = 0;
char *dump_text = NULL;
len = strlen(comment) + strlen(": ") + strlen(rsc->id) + strlen(" utilization on ")
+ strlen(node->details->uname) + strlen(":") + 1;
dump_text = calloc(1, len);
sprintf(dump_text, "%s: %s utilization on %s:", comment, rsc->id, node->details->uname);
g_hash_table_foreach(rsc->utilization, append_dump_text, &dump_text);
if (level == 0) {
fprintf(stdout, "%s\n", dump_text);
} else {
crm_trace("%s", dump_text);
}
free(dump_text);
}
gint
sort_rsc_index(gconstpointer a, gconstpointer b)
{
const resource_t *resource1 = (const resource_t *)a;
const resource_t *resource2 = (const resource_t *)b;
if (a == NULL && b == NULL) {
return 0;
}
if (a == NULL) {
return 1;
}
if (b == NULL) {
return -1;
}
if (resource1->sort_index > resource2->sort_index) {
return -1;
}
if (resource1->sort_index < resource2->sort_index) {
return 1;
}
return 0;
}
gint
sort_rsc_priority(gconstpointer a, gconstpointer b)
{
const resource_t *resource1 = (const resource_t *)a;
const resource_t *resource2 = (const resource_t *)b;
if (a == NULL && b == NULL) {
return 0;
}
if (a == NULL) {
return 1;
}
if (b == NULL) {
return -1;
}
if (resource1->priority > resource2->priority) {
return -1;
}
if (resource1->priority < resource2->priority) {
return 1;
}
return 0;
}
action_t *
custom_action(resource_t * rsc, char *key, const char *task,
node_t * on_node, gboolean optional, gboolean save_action,
pe_working_set_t * data_set)
{
action_t *action = NULL;
GListPtr possible_matches = NULL;
CRM_CHECK(key != NULL, return NULL);
CRM_CHECK(task != NULL, return NULL);
if (save_action && rsc != NULL) {
possible_matches = find_actions(rsc->actions, key, on_node);
}
if (possible_matches != NULL) {
if (g_list_length(possible_matches) > 1) {
pe_warn("Action %s for %s on %s exists %d times",
task, rsc ? rsc->id : "<NULL>",
on_node ? on_node->details->uname : "<NULL>", g_list_length(possible_matches));
}
action = g_list_nth_data(possible_matches, 0);
crm_trace("Found existing action (%d) %s for %s on %s",
action->id, task, rsc ? rsc->id : "<NULL>",
on_node ? on_node->details->uname : "<NULL>");
g_list_free(possible_matches);
}
if (action == NULL) {
if (save_action) {
crm_trace("Creating%s action %d: %s for %s on %s",
optional ? "" : " manditory", data_set->action_id, key,
rsc ? rsc->id : "<NULL>", on_node ? on_node->details->uname : "<NULL>");
}
action = calloc(1, sizeof(action_t));
if (save_action) {
action->id = data_set->action_id++;
} else {
action->id = 0;
}
action->rsc = rsc;
CRM_ASSERT(task != NULL);
action->task = crm_strdup(task);
if (on_node) {
action->node = node_copy(on_node);
}
action->uuid = crm_strdup(key);
pe_set_action_bit(action, pe_action_failure_is_fatal);
pe_set_action_bit(action, pe_action_runnable);
if (optional) {
pe_set_action_bit(action, pe_action_optional);
} else {
pe_clear_action_bit(action, pe_action_optional);
}
/*
Implied by calloc()...
action->actions_before = NULL;
action->actions_after = NULL;
action->pseudo = FALSE;
action->dumped = FALSE;
action->processed = FALSE;
action->seen_count = 0;
*/
action->extra = g_hash_table_new_full(crm_str_hash, g_str_equal, free, free);
action->meta = g_hash_table_new_full(crm_str_hash, g_str_equal, free, free);
if (save_action) {
data_set->actions = g_list_prepend(data_set->actions, action);
}
if (rsc != NULL) {
action->op_entry = find_rsc_op_entry_helper(rsc, key, TRUE);
unpack_operation(action, action->op_entry, data_set);
if (save_action) {
rsc->actions = g_list_prepend(rsc->actions, action);
}
}
if (save_action) {
crm_trace("Action %d created", action->id);
}
}
if (optional == FALSE) {
crm_trace("Action %d (%s) marked manditory", action->id, action->uuid);
pe_clear_action_bit(action, pe_action_optional);
}
if (rsc != NULL) {
enum action_tasks a_task = text2task(action->task);
int warn_level = LOG_DEBUG_3;
if (save_action) {
warn_level = LOG_WARNING;
}
if (is_set(action->flags, pe_action_have_node_attrs) == FALSE
&& action->node != NULL && action->op_entry != NULL) {
pe_set_action_bit(action, pe_action_have_node_attrs);
unpack_instance_attributes(data_set->input, action->op_entry, XML_TAG_ATTR_SETS,
action->node->details->attrs,
action->extra, NULL, FALSE, data_set->now);
}
if (is_set(action->flags, pe_action_pseudo)) {
/* leave untouched */
} else if (action->node == NULL) {
pe_clear_action_bit(action, pe_action_runnable);
} else if (is_not_set(rsc->flags, pe_rsc_managed)
&& g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL) == NULL) {
crm_debug("Action %s (unmanaged)", action->uuid);
pe_set_action_bit(action, pe_action_optional);
/* action->runnable = FALSE; */
} else if (action->node->details->online == FALSE) {
pe_clear_action_bit(action, pe_action_runnable);
do_crm_log(warn_level, "Action %s on %s is unrunnable (offline)",
action->uuid, action->node->details->uname);
if (is_set(action->rsc->flags, pe_rsc_managed)
&& save_action && a_task == stop_rsc) {
do_crm_log(warn_level, "Marking node %s unclean", action->node->details->uname);
action->node->details->unclean = TRUE;
}
} else if (action->node->details->pending) {
pe_clear_action_bit(action, pe_action_runnable);
do_crm_log(warn_level, "Action %s on %s is unrunnable (pending)",
action->uuid, action->node->details->uname);
} else if (action->needs == rsc_req_nothing) {
crm_trace("Action %s doesnt require anything", action->uuid);
pe_set_action_bit(action, pe_action_runnable);
#if 0
/*
* No point checking this
* - if we dont have quorum we cant stonith anyway
*/
} else if (action->needs == rsc_req_stonith) {
crm_trace("Action %s requires only stonith", action->uuid);
action->runnable = TRUE;
#endif
} else if (is_set(data_set->flags, pe_flag_have_quorum) == FALSE
&& data_set->no_quorum_policy == no_quorum_stop) {
pe_clear_action_bit(action, pe_action_runnable);
crm_debug("%s\t%s (cancelled : quorum)", action->node->details->uname, action->uuid);
} else if (is_set(data_set->flags, pe_flag_have_quorum) == FALSE
&& data_set->no_quorum_policy == no_quorum_freeze) {
crm_trace("Check resource is already active");
if (rsc->fns->active(rsc, TRUE) == FALSE) {
pe_clear_action_bit(action, pe_action_runnable);
crm_debug("%s\t%s (cancelled : quorum freeze)",
action->node->details->uname, action->uuid);
}
} else {
crm_trace("Action %s is runnable", action->uuid);
pe_set_action_bit(action, pe_action_runnable);
}
if (save_action) {
switch (a_task) {
case stop_rsc:
set_bit(rsc->flags, pe_rsc_stopping);
break;
case start_rsc:
clear_bit(rsc->flags, pe_rsc_starting);
if (is_set(action->flags, pe_action_runnable)) {
set_bit(rsc->flags, pe_rsc_starting);
}
break;
default:
break;
}
}
}
free(key);
return action;
}
void
unpack_operation(action_t * action, xmlNode * xml_obj, pe_working_set_t * data_set)
{
int value_i = 0;
unsigned long long interval = 0;
unsigned long long start_delay = 0;
char *value_ms = NULL;
const char *class = NULL;
const char *value = NULL;
const char *field = NULL;
CRM_CHECK(action->rsc != NULL, return);
unpack_instance_attributes(data_set->input, data_set->op_defaults, XML_TAG_META_SETS, NULL,
action->meta, NULL, FALSE, data_set->now);
if (xml_obj) {
xmlAttrPtr xIter = NULL;
for (xIter = xml_obj->properties; xIter; xIter = xIter->next) {
const char *prop_name = (const char *)xIter->name;
const char *prop_value = crm_element_value(xml_obj, prop_name);
g_hash_table_replace(action->meta, crm_strdup(prop_name), crm_strdup(prop_value));
}
}
unpack_instance_attributes(data_set->input, xml_obj, XML_TAG_META_SETS,
NULL, action->meta, NULL, FALSE, data_set->now);
unpack_instance_attributes(data_set->input, xml_obj, XML_TAG_ATTR_SETS,
NULL, action->meta, NULL, FALSE, data_set->now);
g_hash_table_remove(action->meta, "id");
class = g_hash_table_lookup(action->rsc->meta, "class");
value = g_hash_table_lookup(action->meta, "requires");
if (safe_str_eq(class, "stonith")) {
action->needs = rsc_req_nothing;
value = "nothing (fencing op)";
} else if (safe_str_eq(value, "nothing")) {
action->needs = rsc_req_nothing;
} else if (safe_str_eq(value, "quorum")) {
action->needs = rsc_req_quorum;
} else if (is_set(data_set->flags, pe_flag_stonith_enabled)
&& safe_str_eq(value, "fencing")) {
action->needs = rsc_req_stonith;
} else {
if (value) {
crm_config_err("Invalid value for %s->requires: %s%s",
action->rsc->id, value,
is_set(data_set->flags,
pe_flag_stonith_enabled) ? "" : " (stonith-enabled=false)");
}
if (safe_str_eq(action->task, CRMD_ACTION_STATUS)
|| safe_str_eq(action->task, CRMD_ACTION_NOTIFY)) {
action->needs = rsc_req_nothing;
value = "nothing (default)";
} else if (data_set->no_quorum_policy == no_quorum_stop
&& safe_str_neq(action->task, CRMD_ACTION_START)) {
action->needs = rsc_req_nothing;
value = "nothing (default)";
} else if (is_set(data_set->flags, pe_flag_stonith_enabled)) {
action->needs = rsc_req_stonith;
value = "fencing (default)";
} else {
action->needs = rsc_req_quorum;
value = "quorum (default)";
}
}
crm_trace("\tAction %s requires: %s", action->task, value);
value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ON_FAIL);
if (safe_str_eq(action->task, CRMD_ACTION_STOP)
&& safe_str_eq(value, "standby")) {
crm_config_err("on-fail=standby is not allowed for stop actions: %s", action->rsc->id);
value = NULL;
}
if (value == NULL) {
} else if (safe_str_eq(value, "block")) {
action->on_fail = action_fail_block;
} else if (safe_str_eq(value, "fence")) {
action->on_fail = action_fail_fence;
value = "node fencing";
if (is_set(data_set->flags, pe_flag_stonith_enabled) == FALSE) {
crm_config_err("Specifying on_fail=fence and" " stonith-enabled=false makes no sense");
action->on_fail = action_fail_stop;
action->fail_role = RSC_ROLE_STOPPED;
value = "stop resource";
}
} else if (safe_str_eq(value, "standby")) {
action->on_fail = action_fail_standby;
value = "node standby";
} else if (safe_str_eq(value, "ignore")
|| safe_str_eq(value, "nothing")) {
action->on_fail = action_fail_ignore;
value = "ignore";
} else if (safe_str_eq(value, "migrate")) {
action->on_fail = action_fail_migrate;
value = "force migration";
} else if (safe_str_eq(value, "stop")) {
action->on_fail = action_fail_stop;
action->fail_role = RSC_ROLE_STOPPED;
value = "stop resource";
} else if (safe_str_eq(value, "restart")) {
action->on_fail = action_fail_recover;
value = "restart (and possibly migrate)";
} else {
pe_err("Resource %s: Unknown failure type (%s)", action->rsc->id, value);
value = NULL;
}
/* defaults */
if (value == NULL && safe_str_eq(action->task, CRMD_ACTION_STOP)) {
if (is_set(data_set->flags, pe_flag_stonith_enabled)) {
action->on_fail = action_fail_fence;
value = "resource fence (default)";
} else {
action->on_fail = action_fail_block;
value = "resource block (default)";
}
} else if (value == NULL) {
action->on_fail = action_fail_recover;
value = "restart (and possibly migrate) (default)";
}
crm_trace("\t%s failure handling: %s", action->task, value);
value = NULL;
if (xml_obj != NULL) {
value = g_hash_table_lookup(action->meta, "role_after_failure");
}
if (value != NULL && action->fail_role == RSC_ROLE_UNKNOWN) {
action->fail_role = text2role(value);
}
/* defaults */
if (action->fail_role == RSC_ROLE_UNKNOWN) {
if (safe_str_eq(action->task, CRMD_ACTION_PROMOTE)) {
action->fail_role = RSC_ROLE_SLAVE;
} else {
action->fail_role = RSC_ROLE_STARTED;
}
}
crm_trace("\t%s failure results in: %s", action->task, role2text(action->fail_role));
field = XML_LRM_ATTR_INTERVAL;
value = g_hash_table_lookup(action->meta, field);
if (value != NULL) {
interval = crm_get_interval(value);
if (interval > 0) {
value_ms = crm_itoa(interval);
g_hash_table_replace(action->meta, crm_strdup(field), value_ms);
} else {
g_hash_table_remove(action->meta, field);
}
}
field = XML_OP_ATTR_START_DELAY;
value = g_hash_table_lookup(action->meta, field);
if (value != NULL) {
value_i = crm_get_msec(value);
if (value_i < 0) {
value_i = 0;
}
start_delay = value_i;
value_ms = crm_itoa(value_i);
g_hash_table_replace(action->meta, crm_strdup(field), value_ms);
} else if (interval > 0 && g_hash_table_lookup(action->meta, XML_OP_ATTR_ORIGIN)) {
char *date_str = NULL;
char *date_str_mutable = NULL;
ha_time_t *origin = NULL;
value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ORIGIN);
date_str = crm_strdup(value);
date_str_mutable = date_str;
origin = parse_date(&date_str_mutable);
free(date_str);
if (origin == NULL) {
crm_config_err("Operation %s contained an invalid " XML_OP_ATTR_ORIGIN ": %s",
ID(xml_obj), value);
} else {
ha_time_t *delay = NULL;
int rc = compare_date(origin, data_set->now);
unsigned long long delay_s = 0;
while (rc < 0) {
add_seconds(origin, interval / 1000);
rc = compare_date(origin, data_set->now);
}
delay = subtract_time(origin, data_set->now);
delay_s = date_in_seconds(delay);
/* log_date(LOG_DEBUG_5, "delay", delay, ha_log_date|ha_log_time|ha_log_local); */
crm_info("Calculated a start delay of %llus for %s", delay_s, ID(xml_obj));
g_hash_table_replace(action->meta, crm_strdup(XML_OP_ATTR_START_DELAY),
crm_itoa(delay_s * 1000));
start_delay = delay_s * 1000;
free_ha_date(origin);
free_ha_date(delay);
}
}
field = XML_ATTR_TIMEOUT;
value = g_hash_table_lookup(action->meta, field);
if (value == NULL) {
value = pe_pref(data_set->config_hash, "default-action-timeout");
}
value_i = crm_get_msec(value);
if (value_i < 0) {
value_i = 0;
}
value_i += start_delay;
value_ms = crm_itoa(value_i);
g_hash_table_replace(action->meta, crm_strdup(field), value_ms);
}
static xmlNode *
find_rsc_op_entry_helper(resource_t * rsc, const char *key, gboolean include_disabled)
{
int number = 0;
gboolean do_retry = TRUE;
char *local_key = NULL;
const char *name = NULL;
const char *value = NULL;
const char *interval = NULL;
char *match_key = NULL;
xmlNode *op = NULL;
xmlNode *operation = NULL;
retry:
for (operation = __xml_first_child(rsc->ops_xml); operation != NULL;
operation = __xml_next(operation)) {
if (crm_str_eq((const char *)operation->name, "op", TRUE)) {
name = crm_element_value(operation, "name");
interval = crm_element_value(operation, XML_LRM_ATTR_INTERVAL);
value = crm_element_value(operation, "enabled");
if (!include_disabled && value && crm_is_true(value) == FALSE) {
continue;
}
number = crm_get_interval(interval);
if (number < 0) {
continue;
}
match_key = generate_op_key(rsc->id, name, number);
if (safe_str_eq(key, match_key)) {
op = operation;
}
free(match_key);
if (op != NULL) {
free(local_key);
return op;
}
}
}
free(local_key);
if (do_retry == FALSE) {
return NULL;
}
do_retry = FALSE;
if (strstr(key, CRMD_ACTION_MIGRATE) || strstr(key, CRMD_ACTION_MIGRATED)) {
local_key = generate_op_key(rsc->id, "migrate", 0);
key = local_key;
goto retry;
} else if (strstr(key, "_notify_")) {
local_key = generate_op_key(rsc->id, "notify", 0);
key = local_key;
goto retry;
}
return NULL;
}
xmlNode *
find_rsc_op_entry(resource_t * rsc, const char *key)
{
return find_rsc_op_entry_helper( rsc, key, FALSE);
}
void
print_node(const char *pre_text, node_t * node, gboolean details)
{
if (node == NULL) {
crm_trace("%s%s: <NULL>", pre_text == NULL ? "" : pre_text, pre_text == NULL ? "" : ": ");
return;
}
crm_trace("%s%s%sNode %s: (weight=%d, fixed=%s)",
pre_text == NULL ? "" : pre_text,
pre_text == NULL ? "" : ": ",
node->details ==
NULL ? "error " : node->details->online ? "" : "Unavailable/Unclean ",
node->details->uname, node->weight, node->fixed ? "True" : "False");
if (details && node != NULL && node->details != NULL) {
char *pe_mutable = crm_strdup("\t\t");
GListPtr gIter = node->details->running_rsc;
crm_trace("\t\t===Node Attributes");
g_hash_table_foreach(node->details->attrs, print_str_str, pe_mutable);
free(pe_mutable);
crm_trace("\t\t=== Resources");
for (; gIter != NULL; gIter = gIter->next) {
resource_t *rsc = (resource_t *) gIter->data;
print_resource(LOG_DEBUG_4, "\t\t", rsc, FALSE);
}
}
}
/*
* Used by the HashTable for-loop
*/
void
print_str_str(gpointer key, gpointer value, gpointer user_data)
{
crm_trace("%s%s %s ==> %s",
user_data == NULL ? "" : (char *)user_data,
user_data == NULL ? "" : ": ", (char *)key, (char *)value);
}
void
print_resource(int log_level, const char *pre_text, resource_t * rsc, gboolean details)
{
long options = pe_print_log;
if (rsc == NULL) {
do_crm_log(log_level - 1, "%s%s: <NULL>",
pre_text == NULL ? "" : pre_text, pre_text == NULL ? "" : ": ");
return;
}
if (details) {
options |= pe_print_details;
}
rsc->fns->print(rsc, pre_text, options, &log_level);
}
void
pe_free_action(action_t * action)
{
if (action == NULL) {
return;
}
- slist_basic_destroy(action->actions_before); /* action_warpper_t* */
- slist_basic_destroy(action->actions_after); /* action_warpper_t* */
+ g_list_free_full(action->actions_before, free); /* action_warpper_t* */
+ g_list_free_full(action->actions_after, free); /* action_warpper_t* */
if (action->extra) {
g_hash_table_destroy(action->extra);
}
if (action->meta) {
g_hash_table_destroy(action->meta);
}
free(action->task);
free(action->uuid);
free(action->node);
free(action);
}
GListPtr
find_recurring_actions(GListPtr input, node_t * not_on_node)
{
const char *value = NULL;
GListPtr result = NULL;
GListPtr gIter = input;
CRM_CHECK(input != NULL, return NULL);
for (; gIter != NULL; gIter = gIter->next) {
action_t *action = (action_t *) gIter->data;
value = g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL);
if (value == NULL) {
/* skip */
} else if (safe_str_eq(value, "0")) {
/* skip */
} else if (safe_str_eq(CRMD_ACTION_CANCEL, action->task)) {
/* skip */
} else if (not_on_node == NULL) {
crm_trace("(null) Found: %s", action->uuid);
result = g_list_prepend(result, action);
} else if (action->node == NULL) {
/* skip */
} else if (action->node->details != not_on_node->details) {
crm_trace("Found: %s", action->uuid);
result = g_list_prepend(result, action);
}
}
return result;
}
enum action_tasks
get_complex_task(resource_t * rsc, const char *name, gboolean allow_non_atomic)
{
enum action_tasks task = text2task(name);
if (rsc == NULL) {
return task;
} else if (allow_non_atomic == FALSE || rsc->variant == pe_native) {
switch (task) {
case stopped_rsc:
case started_rsc:
case action_demoted:
case action_promoted:
crm_trace("Folding %s back into its atomic counterpart for %s", name, rsc->id);
return task - 1;
break;
default:
break;
}
}
return task;
}
action_t *
find_first_action(GListPtr input, const char *uuid, const char *task, node_t * on_node)
{
GListPtr gIter = NULL;
CRM_CHECK(uuid || task, return NULL);
for (gIter = input; gIter != NULL; gIter = gIter->next) {
action_t *action = (action_t *) gIter->data;
if (uuid != NULL && safe_str_neq(uuid, action->uuid)) {
continue;
} else if (task != NULL && safe_str_neq(task, action->task)) {
continue;
} else if (on_node == NULL) {
return action;
} else if (action->node == NULL) {
continue;
} else if (on_node->details == action->node->details) {
return action;
}
}
return NULL;
}
GListPtr
find_actions(GListPtr input, const char *key, node_t * on_node)
{
GListPtr gIter = input;
GListPtr result = NULL;
CRM_CHECK(key != NULL, return NULL);
for (; gIter != NULL; gIter = gIter->next) {
action_t *action = (action_t *) gIter->data;
crm_trace("Matching %s against %s", key, action->uuid);
if (safe_str_neq(key, action->uuid)) {
continue;
} else if (on_node == NULL) {
result = g_list_prepend(result, action);
} else if (action->node == NULL) {
/* skip */
crm_trace("While looking for %s action on %s, "
"found an unallocated one. Assigning"
" it to the requested node...", key, on_node->details->uname);
action->node = node_copy(on_node);
result = g_list_prepend(result, action);
} else if (on_node->details == action->node->details) {
result = g_list_prepend(result, action);
}
}
return result;
}
GListPtr
find_actions_exact(GListPtr input, const char *key, node_t * on_node)
{
GListPtr gIter = input;
GListPtr result = NULL;
CRM_CHECK(key != NULL, return NULL);
for (; gIter != NULL; gIter = gIter->next) {
action_t *action = (action_t *) gIter->data;
crm_trace("Matching %s against %s", key, action->uuid);
if (safe_str_neq(key, action->uuid)) {
crm_trace("Key mismatch: %s vs. %s", key, action->uuid);
continue;
} else if (on_node == NULL || action->node == NULL) {
crm_trace("on_node=%p, action->node=%p", on_node, action->node);
continue;
} else if (safe_str_eq(on_node->details->id, action->node->details->id)) {
result = g_list_prepend(result, action);
}
crm_trace("Node mismatch: %s vs. %s", on_node->details->id, action->node->details->id);
}
return result;
}
static void
resource_node_score(resource_t * rsc, node_t * node, int score, const char *tag)
{
node_t *match = NULL;
if (rsc->children) {
GListPtr gIter = rsc->children;
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t *) gIter->data;
resource_node_score(child_rsc, node, score, tag);
}
}
crm_trace("Setting %s for %s on %s: %d", tag, rsc->id, node->details->uname, score);
match = pe_hash_table_lookup(rsc->allowed_nodes, node->details->id);
if (match == NULL) {
match = node_copy(node);
match->weight = merge_weights(score, node->weight);
g_hash_table_insert(rsc->allowed_nodes, (gpointer) match->details->id, match);
}
match->weight = merge_weights(match->weight, score);
}
void
resource_location(resource_t * rsc, node_t * node, int score, const char *tag,
pe_working_set_t * data_set)
{
if (node != NULL) {
resource_node_score(rsc, node, score, tag);
} else if (data_set != NULL) {
GListPtr gIter = data_set->nodes;
for (; gIter != NULL; gIter = gIter->next) {
node_t *node = (node_t *) gIter->data;
resource_node_score(rsc, node, score, tag);
}
} else {
GHashTableIter iter;
node_t *node = NULL;
g_hash_table_iter_init(&iter, rsc->allowed_nodes);
while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
resource_node_score(rsc, node, score, tag);
}
}
if (node == NULL && score == -INFINITY) {
if (rsc->allocated_to) {
crm_info("Deallocating %s from %s", rsc->id, rsc->allocated_to->details->uname);
free(rsc->allocated_to);
rsc->allocated_to = NULL;
}
}
}
#define sort_return(an_int, why) do { \
free(a_uuid); \
free(b_uuid); \
crm_trace("%s (%d) %c %s (%d) : %s", \
a_xml_id, a_call_id, an_int>0?'>':an_int<0?'<':'=', \
b_xml_id, b_call_id, why); \
return an_int; \
} while(0)
gint
sort_op_by_callid(gconstpointer a, gconstpointer b)
{
int a_call_id = -1;
int b_call_id = -1;
char *a_uuid = NULL;
char *b_uuid = NULL;
const xmlNode *xml_a = a;
const xmlNode *xml_b = b;
const char *a_xml_id = crm_element_value_const(xml_a, XML_ATTR_ID);
const char *b_xml_id = crm_element_value_const(xml_b, XML_ATTR_ID);
if (safe_str_eq(a_xml_id, b_xml_id)) {
/* We have duplicate lrm_rsc_op entries in the status
* section which is unliklely to be a good thing
* - we can handle it easily enough, but we need to get
* to the bottom of why its happening.
*/
pe_err("Duplicate lrm_rsc_op entries named %s", a_xml_id);
sort_return(0, "duplicate");
}
crm_element_value_const_int(xml_a, XML_LRM_ATTR_CALLID, &a_call_id);
crm_element_value_const_int(xml_b, XML_LRM_ATTR_CALLID, &b_call_id);
if (a_call_id == -1 && b_call_id == -1) {
/* both are pending ops so it doesnt matter since
* stops are never pending
*/
sort_return(0, "pending");
} else if (a_call_id >= 0 && a_call_id < b_call_id) {
sort_return(-1, "call id");
} else if (b_call_id >= 0 && a_call_id > b_call_id) {
sort_return(1, "call id");
} else if (b_call_id >= 0 && a_call_id == b_call_id) {
/*
* The op and last_failed_op are the same
* Order on last-rc-change
*/
int last_a = -1;
int last_b = -1;
crm_element_value_const_int(xml_a, "last-rc-change", &last_a);
crm_element_value_const_int(xml_b, "last-rc-change", &last_b);
if (last_a >= 0 && last_a < last_b) {
sort_return(-1, "rc-change");
} else if (last_b >= 0 && last_a > last_b) {
sort_return(1, "rc-change");
}
sort_return(0, "rc-change");
} else {
/* One of the inputs is a pending operation
* Attempt to use XML_ATTR_TRANSITION_MAGIC to determine its age relative to the other
*/
int a_id = -1;
int b_id = -1;
int dummy = -1;
const char *a_magic = crm_element_value_const(xml_a, XML_ATTR_TRANSITION_MAGIC);
const char *b_magic = crm_element_value_const(xml_b, XML_ATTR_TRANSITION_MAGIC);
CRM_CHECK(a_magic != NULL && b_magic != NULL, sort_return(0, "No magic"));
CRM_CHECK(decode_transition_magic(a_magic, &a_uuid, &a_id, &dummy, &dummy, &dummy, &dummy),
sort_return(0, "bad magic a"));
CRM_CHECK(decode_transition_magic(b_magic, &b_uuid, &b_id, &dummy, &dummy, &dummy, &dummy),
sort_return(0, "bad magic b"));
/* try and determin the relative age of the operation...
* some pending operations (ie. a start) may have been supuerceeded
* by a subsequent stop
*
* [a|b]_id == -1 means its a shutdown operation and _always_ comes last
*/
if (safe_str_neq(a_uuid, b_uuid) || a_id == b_id) {
/*
* some of the logic in here may be redundant...
*
* if the UUID from the TE doesnt match then one better
* be a pending operation.
* pending operations dont survive between elections and joins
* because we query the LRM directly
*/
if (b_call_id == -1) {
sort_return(-1, "transition + call");
} else if (a_call_id == -1) {
sort_return(1, "transition + call");
}
} else if ((a_id >= 0 && a_id < b_id) || b_id == -1) {
sort_return(-1, "transition");
} else if ((b_id >= 0 && a_id > b_id) || a_id == -1) {
sort_return(1, "transition");
}
}
/* we should never end up here */
CRM_CHECK(FALSE, sort_return(0, "default"));
}
time_t
get_timet_now(pe_working_set_t * data_set)
{
time_t now = 0;
if (data_set && data_set->now) {
now = data_set->now->tm_now;
}
if (now == 0) {
/* eventually we should convert data_set->now into time_tm
* for now, its only triggered by PE regression tests
*/
now = time(NULL);
crm_crit("Defaulting to 'now'");
if (data_set && data_set->now) {
data_set->now->tm_now = now;
}
}
return now;
}
struct fail_search {
resource_t *rsc;
int count;
long long last;
char *key;
};
static void
get_failcount_by_prefix(gpointer key_p, gpointer value, gpointer user_data)
{
struct fail_search *search = user_data;
const char *key = key_p;
const char *match = strstr(key, search->key);
if (match) {
if (strstr(key, "last-failure-") == key && (key + 13) == match) {
search->last = crm_int_helper(value, NULL);
} else if (strstr(key, "fail-count-") == key && (key + 11) == match) {
search->count += char2score(value);
}
}
}
int
get_failcount(node_t * node, resource_t * rsc, int *last_failure, pe_working_set_t * data_set)
{
struct fail_search search = { rsc, 0, 0, NULL };
search.key = crm_strdup(rsc->id);
if (is_not_set(rsc->flags, pe_rsc_unique)) {
int lpc = 0;
search.rsc = uber_parent(rsc);
/* Strip the clone incarnation */
for (lpc = strlen(search.key); lpc > 0; lpc--) {
if (search.key[lpc] == ':') {
search.key[lpc + 1] = 0;
break;
}
}
g_hash_table_foreach(node->details->attrs, get_failcount_by_prefix, &search);
} else {
/* Optimize the "normal" case */
char *key = NULL;
const char *value = NULL;
key = crm_concat("fail-count", rsc->id, '-');
value = g_hash_table_lookup(node->details->attrs, key);
search.count = char2score(value);
free(key);
key = crm_concat("last-failure", rsc->id, '-');
value = g_hash_table_lookup(node->details->attrs, key);
search.last = crm_int_helper(value, NULL);
free(key);
}
if (search.count != 0 && search.last != 0 && rsc->failure_timeout) {
if (last_failure) {
*last_failure = search.last;
}
if (search.last > 0) {
time_t now = get_timet_now(data_set);
if (now > (search.last + rsc->failure_timeout)) {
crm_notice("Failcount for %s on %s has expired (limit was %ds)",
search.rsc->id, node->details->uname, rsc->failure_timeout);
search.count = 0;
}
}
}
if (search.count != 0) {
char *score = score2char(search.count);
crm_info("%s has failed %s times on %s", search.rsc->id, score, node->details->uname);
free(score);
}
free(search.key);
return search.count;
}
gboolean
get_target_role(resource_t * rsc, enum rsc_role_e * role)
{
enum rsc_role_e local_role = RSC_ROLE_UNKNOWN;
const char *value = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE);
CRM_CHECK(role != NULL, return FALSE);
if (value == NULL || safe_str_eq("started", value)
|| safe_str_eq("default", value)) {
return FALSE;
}
local_role = text2role(value);
if (local_role == RSC_ROLE_UNKNOWN) {
crm_config_err("%s: Unknown value for %s: %s", rsc->id, XML_RSC_ATTR_TARGET_ROLE, value);
return FALSE;
} else if (local_role > RSC_ROLE_STARTED) {
if (uber_parent(rsc)->variant == pe_master) {
if (local_role > RSC_ROLE_SLAVE) {
/* This is what we'd do anyway, just leave the default to avoid messing up the placement algorithm */
return FALSE;
}
} else {
crm_config_err("%s is not part of a master/slave resource, a %s of '%s' makes no sense",
rsc->id, XML_RSC_ATTR_TARGET_ROLE, value);
return FALSE;
}
}
*role = local_role;
return TRUE;
}
gboolean
order_actions(action_t * lh_action, action_t * rh_action, enum pe_ordering order)
{
GListPtr gIter = NULL;
action_wrapper_t *wrapper = NULL;
GListPtr list = NULL;
if (order == pe_order_none) {
return FALSE;
}
if (lh_action == NULL || rh_action == NULL) {
return FALSE;
}
crm_trace("Ordering Action %s before %s", lh_action->uuid, rh_action->uuid);
/* Filter dups, otherwise update_action_states() has too much work to do */
gIter = lh_action->actions_after;
for (; gIter != NULL; gIter = gIter->next) {
action_wrapper_t *after = (action_wrapper_t *) gIter->data;
if (after->action == rh_action && (after->type & order)) {
return FALSE;
}
}
wrapper = calloc(1, sizeof(action_wrapper_t));
wrapper->action = rh_action;
wrapper->type = order;
list = lh_action->actions_after;
list = g_list_prepend(list, wrapper);
lh_action->actions_after = list;
wrapper = NULL;
/* order |= pe_order_implies_then; */
/* order ^= pe_order_implies_then; */
wrapper = calloc(1, sizeof(action_wrapper_t));
wrapper->action = lh_action;
wrapper->type = order;
list = rh_action->actions_before;
list = g_list_prepend(list, wrapper);
rh_action->actions_before = list;
return TRUE;
}
action_t *
get_pseudo_op(const char *name, pe_working_set_t * data_set)
{
action_t *op = NULL;
const char *op_s = name;
GListPtr possible_matches = NULL;
possible_matches = find_actions(data_set->actions, name, NULL);
if (possible_matches != NULL) {
if (g_list_length(possible_matches) > 1) {
pe_warn("Action %s exists %d times", name, g_list_length(possible_matches));
}
op = g_list_nth_data(possible_matches, 0);
g_list_free(possible_matches);
} else {
op = custom_action(NULL, crm_strdup(op_s), op_s, NULL, TRUE, TRUE, data_set);
set_bit_inplace(op->flags, pe_action_pseudo);
set_bit_inplace(op->flags, pe_action_runnable);
}
return op;
}
void
destroy_ticket(gpointer data)
{
ticket_t *ticket = data;
if (ticket->state) {
g_hash_table_destroy(ticket->state);
}
free(ticket->id);
free(ticket);
}
ticket_t *
ticket_new(const char *ticket_id, pe_working_set_t * data_set)
{
ticket_t *ticket = NULL;
if (ticket_id == NULL || strlen(ticket_id) == 0) {
return NULL;
}
if (data_set->tickets == NULL) {
data_set->tickets =
g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, destroy_ticket);
}
ticket = g_hash_table_lookup(data_set->tickets, ticket_id);
if (ticket == NULL) {
ticket = calloc(1, sizeof(ticket_t));
if (ticket == NULL) {
crm_err("Cannot allocate ticket '%s'", ticket_id);
return NULL;
}
crm_trace("Creaing ticket entry for %s", ticket_id);
ticket->id = crm_strdup(ticket_id);
ticket->granted = FALSE;
ticket->last_granted = -1;
ticket->standby = FALSE;
ticket->state = g_hash_table_new_full(crm_str_hash, g_str_equal,
g_hash_destroy_str, g_hash_destroy_str);
g_hash_table_insert(data_set->tickets, crm_strdup(ticket->id), ticket);
}
return ticket;
}
diff --git a/lib/services/services.c b/lib/services/services.c
index 9334373c6d..c9618e0247 100644
--- a/lib/services/services.c
+++ b/lib/services/services.c
@@ -1,476 +1,476 @@
/*
* Copyright (C) 2010 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 <crm_internal.h>
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#include <crm/crm.h>
#include <crm/common/mainloop.h>
#include <crm/services.h>
#include "services_private.h"
#if SUPPORT_UPSTART
#include <upstart.h>
#endif
#if SUPPORT_SYSTEMD
#include <systemd.h>
#endif
/* TODO: Develop a rollover strategy */
static int operations = 0;
GHashTable *recurring_actions = NULL;
svc_action_t *
services_action_create(const char *name, const char *action, int interval,
int timeout)
{
return resources_action_create(name, "lsb", NULL, name, action, interval, timeout, NULL);
}
svc_action_t *resources_action_create(
const char *name, const char *standard, const char *provider, const char *agent,
const char *action, int interval, int timeout, GHashTable *params)
{
svc_action_t *op;
/*
* Do some up front sanity checks before we go off and
* build the svc_action_t instance.
*/
if (crm_strlen_zero(name)) {
crm_err("A service or resource action must have a name.");
return NULL;
}
if (crm_strlen_zero(standard)) {
crm_err("A service action must have a valid standard.");
return NULL;
}
if (!strcasecmp(standard, "ocf") && crm_strlen_zero(provider)) {
crm_err("An OCF resource action must have a provider.");
return NULL;
}
if (crm_strlen_zero(agent)) {
crm_err("A service or resource action must have an agent.");
return NULL;
}
if (crm_strlen_zero(action)) {
crm_err("A service or resource action must specify an action.");
return NULL;
}
if (safe_str_eq(action, "monitor") && (safe_str_eq(standard, "lsb") || safe_str_eq(standard, "service"))) {
action = "status";
}
/*
* Sanity checks passed, proceed!
*/
op = calloc(1, sizeof(svc_action_t));
op->opaque = calloc(1, sizeof(svc_action_private_t));
op->rsc = strdup(name);
op->action = strdup(action);
op->interval = interval;
op->timeout = timeout;
op->standard = strdup(standard);
op->agent = strdup(agent);
op->sequence = ++operations;
if (asprintf(&op->id, "%s_%s_%d", name, action, interval) == -1) {
goto return_error;
}
if(strcasecmp(op->standard, "service") == 0) {
/* Work it out and then fall into the if-else block below.
* Priority is:
* - lsb
* - systemd
* - upstart
*/
int rc = 0;
struct stat st;
char *path = NULL;
#ifdef LSB_ROOT_DIR
rc = asprintf(&path, "%s/%s", LSB_ROOT_DIR, op->agent);
if(rc > 0 && stat(path, &st) == 0) {
crm_debug("Found an lsb agent for %s/% the", op->rsc, op->agent);
free(path);
free(op->standard);
op->standard = strdup("lsb");
goto expanded;
}
free(path);
#endif
#if SUPPORT_SYSTEMD
if(systemd_unit_exists(op->agent)) {
crm_debug("Found a systemd agent for %s/%s", op->rsc, op->agent);
free(op->standard);
op->standard = strdup("systemd");
goto expanded;
}
#endif
#if SUPPORT_UPSTART
if(upstart_job_exists(op->agent)) {
crm_debug("Found an upstart agent for %s/%s", op->rsc, op->agent);
free(op->standard);
op->standard = strdup("upstart");
goto expanded;
}
#endif
crm_info("Cannot determine the standard for %s (%s)", op->rsc, op->agent);
}
expanded:
if(strcasecmp(op->standard, "ocf") == 0) {
op->provider = strdup(provider);
op->params = params;
if (asprintf(&op->opaque->exec, "%s/resource.d/%s/%s",
OCF_ROOT_DIR, provider, agent) == -1) {
goto return_error;
}
op->opaque->args[0] = strdup(op->opaque->exec);
op->opaque->args[1] = strdup(action);
} else if(strcasecmp(op->standard, "lsb") == 0) {
if (op->agent[0] == '/') {
/* if given an absolute path, use that instead
* of tacking on the LSB_ROOT_DIR path to the front */
op->opaque->exec = crm_strdup(op->agent);
} else if (asprintf(&op->opaque->exec, "%s/%s", LSB_ROOT_DIR, op->agent) == -1) {
goto return_error;
}
op->opaque->args[0] = strdup(op->opaque->exec);
op->opaque->args[1] = strdup(op->action);
op->opaque->args[2] = NULL;
#if SUPPORT_SYSTEMD
} else if(strcasecmp(op->standard, "systemd") == 0) {
op->opaque->exec = strdup("systemd-dbus");
#endif
#if SUPPORT_UPSTART
} else if(strcasecmp(op->standard, "upstart") == 0) {
op->opaque->exec = strdup("upstart-dbus");
#endif
} else if(strcasecmp(op->standard, "service") == 0) {
op->opaque->exec = strdup(SERVICE_SCRIPT);
op->opaque->args[0] = strdup(SERVICE_SCRIPT);
op->opaque->args[1] = strdup(agent);
op->opaque->args[2] = strdup(action);
} else {
crm_err("Unknown resource standard: %s", op->standard);
services_action_free(op);
op = NULL;
}
return op;
return_error:
services_action_free(op);
return NULL;
}
svc_action_t *
services_action_create_generic(const char *exec, const char *args[])
{
svc_action_t *op;
unsigned int cur_arg;
op = calloc(1, sizeof(*op));
op->opaque = calloc(1, sizeof(svc_action_private_t));
op->opaque->exec = strdup(exec);
op->opaque->args[0] = strdup(exec);
for (cur_arg = 1; args && args[cur_arg - 1]; cur_arg++) {
op->opaque->args[cur_arg] = strdup(args[cur_arg - 1]);
if (cur_arg == DIMOF(op->opaque->args) - 1) {
crm_err("svc_action_t args list not long enough for '%s' execution request.", exec);
break;
}
}
return op;
}
void
services_action_free(svc_action_t *op)
{
unsigned int i;
if (op == NULL) {
return;
}
if (op->opaque->stderr_gsource) {
mainloop_del_fd(op->opaque->stderr_gsource);
op->opaque->stderr_gsource = NULL;
}
if (op->opaque->stdout_gsource) {
mainloop_del_fd(op->opaque->stdout_gsource);
op->opaque->stdout_gsource = NULL;
}
free(op->id);
free(op->opaque->exec);
for (i = 0; i < DIMOF(op->opaque->args); i++) {
free(op->opaque->args[i]);
}
free(op->rsc);
free(op->action);
free(op->standard);
free(op->agent);
free(op->provider);
free(op->stdout_data);
free(op->stderr_data);
if (op->params) {
g_hash_table_destroy(op->params);
op->params = NULL;
}
free(op);
}
gboolean
cancel_recurring_action(svc_action_t *op)
{
if (op->pid) {
return FALSE;
}
crm_info("Cancelling operation %s", op->id);
if (recurring_actions) {
g_hash_table_remove(recurring_actions, op->id);
}
if (op->opaque->repeat_timer) {
g_source_remove(op->opaque->repeat_timer);
}
return TRUE;
}
gboolean
services_action_cancel(const char *name, const char *action, int interval)
{
svc_action_t* op = NULL;
char id[512];
snprintf(id, sizeof(id), "%s_%s_%d", name, action, interval);
if (!(op = g_hash_table_lookup(recurring_actions, id))) {
return FALSE;
}
if (cancel_recurring_action(op)) {
op->status = PCMK_LRM_OP_CANCELLED;
if (op->opaque->callback) {
op->opaque->callback(op);
}
services_action_free(op);
} else {
crm_info("Cancelling op: %s will occur once operation completes", id);
op->cancel = 1;
}
return TRUE;
}
gboolean
services_action_async(svc_action_t* op, void (*action_callback)(svc_action_t *))
{
if (action_callback) {
op->opaque->callback = action_callback;
}
if (recurring_actions == NULL) {
recurring_actions = g_hash_table_new_full(g_str_hash, g_str_equal,
NULL, NULL);
}
if (op->interval > 0) {
g_hash_table_replace(recurring_actions, op->id, op);
}
#if SUPPORT_UPSTART
if(strcasecmp(op->standard, "upstart") == 0) {
return upstart_job_exec(op, FALSE);
}
#endif
#if SUPPORT_SYSTEMD
if(strcasecmp(op->standard, "systemd") == 0) {
return systemd_unit_exec(op, FALSE);
}
#endif
return services_os_action_execute(op, FALSE);
}
gboolean
services_action_sync(svc_action_t* op)
{
gboolean rc = TRUE;
if(strcasecmp(op->standard, "upstart") == 0) {
#if SUPPORT_UPSTART
rc = upstart_job_exec(op, TRUE);
#endif
} else if(strcasecmp(op->standard, "systemd") == 0) {
#if SUPPORT_SYSTEMD
rc = systemd_unit_exec(op, TRUE);
#endif
} else {
rc = services_os_action_execute(op, TRUE);
}
crm_trace(" > %s_%s_%d: %s = %d", op->rsc, op->action, op->interval,
op->opaque->exec, op->rc);
if (op->stdout_data) {
crm_trace(" > stdout: %s", op->stdout_data);
}
if (op->stderr_data) {
crm_trace(" > stderr: %s", op->stderr_data);
}
return rc;
}
GList *
get_directory_list(const char *root, gboolean files)
{
return services_os_get_directory_list(root, files);
}
GList *
services_list(void)
{
return resources_list_agents("lsb", NULL);
}
GList *
resources_list_standards(void)
{
GList *standards = NULL;
GList *agents = NULL;
standards = g_list_append(standards, strdup("ocf"));
standards = g_list_append(standards, strdup("lsb"));
standards = g_list_append(standards, strdup("service"));
#if SUPPORT_SYSTEMD
agents = systemd_unit_listall();
if(agents) {
standards = g_list_append(standards, strdup("systemd"));
- slist_basic_destroy(agents);
+ g_list_free_full(agents, free);
}
#endif
#if SUPPORT_UPSTART
agents = upstart_job_listall();
if(agents) {
standards = g_list_append(standards, strdup("upstart"));
- slist_basic_destroy(agents);
+ g_list_free_full(agents, free);
}
#endif
agents = NULL; /* Keep the compiler happy */
return standards;
}
GList *
resources_list_providers(const char *standard)
{
if (strcasecmp(standard, "ocf") == 0) {
return resources_os_list_ocf_providers();
}
return NULL;
}
GList *
resources_list_agents(const char *standard, const char *provider)
{
if (strcasecmp(standard, "ocf") == 0) {
return resources_os_list_ocf_agents(provider);
} else if (strcasecmp(standard, "lsb") == 0) {
return resources_os_list_lsb_agents();
#if SUPPORT_UPSTART
} else if (strcasecmp(standard, "systemd") == 0) {
return systemd_unit_listall();
#endif
#if SUPPORT_UPSTART
} else if (strcasecmp(standard, "upstart") == 0) {
return upstart_job_listall();
#endif
} else if (strcasecmp(standard, "service") == 0) {
GList *tmp1;
GList *tmp2;
GList *result = resources_os_list_lsb_agents();
tmp1 = NULL; /* Keep the compiler happy */
tmp2 = NULL; /* Keep the compiler happy */
#if SUPPORT_SYSTEMD
tmp1 = result;
tmp2 = systemd_unit_listall();
if(tmp2) {
result = g_list_concat(tmp1, tmp2);
}
#endif
#if SUPPORT_UPSTART
tmp1 = result;
tmp2 = upstart_job_listall();
if(tmp2) {
result = g_list_concat(tmp1, tmp2);
}
#endif
return result;
}
return NULL;
}
diff --git a/pengine/allocate.c b/pengine/allocate.c
index f0816e30cb..0331246853 100644
--- a/pengine/allocate.c
+++ b/pengine/allocate.c
@@ -1,2270 +1,2270 @@
/*
* 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/cib.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <glib.h>
#include <crm/pengine/status.h>
#include <pengine.h>
#include <allocate.h>
#include <utils.h>
#include <lib/pengine/utils.h>
CRM_TRACE_INIT_DATA(pe_allocate);
void set_alloc_actions(pe_working_set_t * data_set);
void migrate_reload_madness(pe_working_set_t * data_set);
resource_alloc_functions_t resource_class_alloc_functions[] = {
{
native_merge_weights,
native_color,
native_create_actions,
native_create_probe,
native_internal_constraints,
native_rsc_colocation_lh,
native_rsc_colocation_rh,
native_rsc_location,
native_action_flags,
native_update_actions,
native_expand,
native_append_meta,
},
{
group_merge_weights,
group_color,
group_create_actions,
native_create_probe,
group_internal_constraints,
group_rsc_colocation_lh,
group_rsc_colocation_rh,
group_rsc_location,
group_action_flags,
group_update_actions,
group_expand,
group_append_meta,
},
{
native_merge_weights,
clone_color,
clone_create_actions,
clone_create_probe,
clone_internal_constraints,
clone_rsc_colocation_lh,
clone_rsc_colocation_rh,
clone_rsc_location,
clone_action_flags,
clone_update_actions,
clone_expand,
clone_append_meta,
},
{
native_merge_weights,
master_color,
master_create_actions,
clone_create_probe,
master_internal_constraints,
clone_rsc_colocation_lh,
master_rsc_colocation_rh,
clone_rsc_location,
clone_action_flags,
clone_update_actions,
clone_expand,
master_append_meta,
}
};
static gboolean
check_rsc_parameters(resource_t * rsc, node_t * node, xmlNode * rsc_entry,
pe_working_set_t * data_set)
{
int attr_lpc = 0;
gboolean force_restart = FALSE;
gboolean delete_resource = FALSE;
const char *value = NULL;
const char *old_value = NULL;
const char *attr_list[] = {
XML_ATTR_TYPE,
XML_AGENT_ATTR_CLASS,
XML_AGENT_ATTR_PROVIDER
};
for (; attr_lpc < DIMOF(attr_list); attr_lpc++) {
value = crm_element_value(rsc->xml, attr_list[attr_lpc]);
old_value = crm_element_value(rsc_entry, attr_list[attr_lpc]);
if (value == old_value /* ie. NULL */
|| crm_str_eq(value, old_value, TRUE)) {
continue;
}
force_restart = TRUE;
crm_notice("Forcing restart of %s on %s, %s changed: %s -> %s",
rsc->id, node->details->uname, attr_list[attr_lpc],
crm_str(old_value), crm_str(value));
}
if (force_restart) {
/* make sure the restart happens */
stop_action(rsc, node, FALSE);
set_bit(rsc->flags, pe_rsc_start_pending);
delete_resource = TRUE;
}
return delete_resource;
}
static void
CancelXmlOp(resource_t * rsc, xmlNode * xml_op, node_t * active_node,
const char *reason, pe_working_set_t * data_set)
{
int interval = 0;
action_t *cancel = NULL;
char *key = NULL;
const char *task = NULL;
const char *call_id = NULL;
const char *interval_s = NULL;
CRM_CHECK(xml_op != NULL, return);
CRM_CHECK(active_node != NULL, return);
task = crm_element_value(xml_op, XML_LRM_ATTR_TASK);
call_id = crm_element_value(xml_op, XML_LRM_ATTR_CALLID);
interval_s = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL);
interval = crm_parse_int(interval_s, "0");
/* we need to reconstruct the key because of the way we used to construct resource IDs */
key = generate_op_key(rsc->id, task, interval);
crm_info("Action %s on %s will be stopped: %s",
key, active_node->details->uname, reason ? reason : "unknown");
cancel = custom_action(rsc, crm_strdup(key), RSC_CANCEL, active_node, FALSE, TRUE, data_set);
free(cancel->task);
cancel->task = crm_strdup(RSC_CANCEL);
add_hash_param(cancel->meta, XML_LRM_ATTR_TASK, task);
add_hash_param(cancel->meta, XML_LRM_ATTR_CALLID, call_id);
add_hash_param(cancel->meta, XML_LRM_ATTR_INTERVAL, interval_s);
custom_action_order(rsc, stop_key(rsc), NULL, rsc, NULL, cancel, pe_order_optional, data_set);
free(key);
key = NULL;
}
static gboolean
check_action_definition(resource_t * rsc, node_t * active_node, xmlNode * xml_op,
pe_working_set_t * data_set)
{
char *key = NULL;
int interval = 0;
const char *interval_s = NULL;
gboolean did_change = FALSE;
xmlNode *params_all = NULL;
xmlNode *params_restart = NULL;
GHashTable *local_rsc_params = NULL;
char *digest_all_calc = NULL;
const char *digest_all = NULL;
const char *restart_list = NULL;
const char *digest_restart = NULL;
char *digest_restart_calc = NULL;
action_t *action = NULL;
const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK);
const char *op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION);
CRM_CHECK(active_node != NULL, return FALSE);
if (safe_str_eq(task, RSC_STOP)) {
return FALSE;
}
interval_s = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL);
interval = crm_parse_int(interval_s, "0");
/* we need to reconstruct the key because of the way we used to construct resource IDs */
key = generate_op_key(rsc->id, task, interval);
if (interval > 0) {
xmlNode *op_match = NULL;
crm_trace("Checking parameters for %s", key);
op_match = find_rsc_op_entry(rsc, key);
if (op_match == NULL && is_set(data_set->flags, pe_flag_stop_action_orphans)) {
CancelXmlOp(rsc, xml_op, active_node, "orphan", data_set);
free(key);
return TRUE;
} else if (op_match == NULL) {
crm_debug("Orphan action detected: %s on %s", key, active_node->details->uname);
free(key);
return TRUE;
}
}
action = custom_action(rsc, key, task, active_node, TRUE, FALSE, data_set);
/* key is free'd by custom_action() */
local_rsc_params = g_hash_table_new_full(crm_str_hash, g_str_equal,
g_hash_destroy_str, g_hash_destroy_str);
get_rsc_attributes(local_rsc_params, rsc, active_node, data_set);
params_all = create_xml_node(NULL, XML_TAG_PARAMS);
g_hash_table_foreach(local_rsc_params, hash2field, params_all);
g_hash_table_foreach(action->extra, hash2field, params_all);
g_hash_table_foreach(rsc->parameters, hash2field, params_all);
g_hash_table_foreach(action->meta, hash2metafield, params_all);
filter_action_parameters(params_all, op_version);
digest_all_calc = calculate_operation_digest(params_all, op_version);
digest_all = crm_element_value(xml_op, XML_LRM_ATTR_OP_DIGEST);
digest_restart = crm_element_value(xml_op, XML_LRM_ATTR_RESTART_DIGEST);
restart_list = crm_element_value(xml_op, XML_LRM_ATTR_OP_RESTART);
if (interval == 0 && safe_str_eq(task, RSC_STATUS)) {
/* Reload based on the start action not a probe */
task = RSC_START;
} else if (interval == 0 && safe_str_eq(task, RSC_MIGRATED)) {
/* Reload based on the start action not a migrate */
task = RSC_START;
}
if (digest_restart) {
/* Changes that force a restart */
params_restart = copy_xml(params_all);
if (restart_list) {
filter_reload_parameters(params_restart, restart_list);
}
digest_restart_calc = calculate_operation_digest(params_restart, op_version);
if (safe_str_neq(digest_restart_calc, digest_restart)) {
did_change = TRUE;
key = generate_op_key(rsc->id, task, interval);
crm_log_xml_info(params_restart, "params:restart");
crm_info("Parameters to %s on %s changed: was %s vs. now %s (restart:%s) %s",
key, active_node->details->uname,
crm_str(digest_restart), digest_restart_calc,
op_version, crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC));
custom_action(rsc, key, task, NULL, FALSE, TRUE, data_set);
goto cleanup;
}
}
if (safe_str_neq(digest_all_calc, digest_all)) {
/* Changes that can potentially be handled by a reload */
did_change = TRUE;
crm_log_xml_info(params_all, "params:reload");
key = generate_op_key(rsc->id, task, interval);
crm_info("Parameters to %s on %s changed: was %s vs. now %s (reload:%s) %s",
key, active_node->details->uname,
crm_str(digest_all), digest_all_calc, op_version,
crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC));
if (interval > 0) {
action_t *op = NULL;
#if 0
/* Always reload/restart the entire resource */
op = custom_action(rsc, start_key(rsc), RSC_START, NULL, FALSE, TRUE, data_set);
update_action_flags(op, pe_action_allow_reload_conversion);
#else
/* Re-sending the recurring op is sufficient - the old one will be cancelled automatically */
op = custom_action(rsc, key, task, NULL, FALSE, TRUE, data_set);
custom_action_order(rsc, start_key(rsc), NULL,
NULL, NULL, op, pe_order_runnable_left, data_set);
#endif
} else if (digest_restart) {
crm_trace("Reloading '%s' action for resource %s", task, rsc->id);
/* Allow this resource to reload - unless something else causes a full restart */
set_bit(rsc->flags, pe_rsc_try_reload);
/* Create these for now, it keeps the action IDs the same in the regression outputs */
custom_action(rsc, key, task, NULL, TRUE, TRUE, data_set);
} else {
crm_trace("Resource %s doesn't know how to reload", rsc->id);
/* Re-send the start/demote/promote op
* Recurring ops will be detected independantly
*/
custom_action(rsc, key, task, NULL, FALSE, TRUE, data_set);
}
}
cleanup:
free_xml(params_all);
free_xml(params_restart);
free(digest_all_calc);
free(digest_restart_calc);
g_hash_table_destroy(local_rsc_params);
pe_free_action(action);
return did_change;
}
extern gboolean DeleteRsc(resource_t * rsc, node_t * node, gboolean optional,
pe_working_set_t * data_set);
static void
check_actions_for(xmlNode * rsc_entry, resource_t * rsc, node_t * node, pe_working_set_t * data_set)
{
GListPtr gIter = NULL;
int offset = -1;
int interval = 0;
int stop_index = 0;
int start_index = 0;
const char *task = NULL;
const char *interval_s = NULL;
xmlNode *rsc_op = NULL;
GListPtr op_list = NULL;
GListPtr sorted_op_list = NULL;
gboolean is_probe = FALSE;
gboolean did_change = FALSE;
CRM_CHECK(node != NULL, return);
if (is_set(rsc->flags, pe_rsc_orphan)) {
crm_trace("Skipping param check for %s: orphan", rsc->id);
return;
} else if (pe_find_node_id(rsc->running_on, node->details->id) == NULL) {
crm_trace("Skipping param check for %s: no longer active on %s",
rsc->id, node->details->uname);
return;
}
crm_trace("Processing %s on %s", rsc->id, node->details->uname);
if (check_rsc_parameters(rsc, node, rsc_entry, data_set)) {
DeleteRsc(rsc, node, FALSE, data_set);
}
for (rsc_op = __xml_first_child(rsc_entry); rsc_op != NULL; rsc_op = __xml_next(rsc_op)) {
if (crm_str_eq((const char *)rsc_op->name, XML_LRM_TAG_RSC_OP, TRUE)) {
op_list = g_list_prepend(op_list, rsc_op);
}
}
sorted_op_list = g_list_sort(op_list, sort_op_by_callid);
calculate_active_ops(sorted_op_list, &start_index, &stop_index);
for (gIter = sorted_op_list; gIter != NULL; gIter = gIter->next) {
xmlNode *rsc_op = (xmlNode *) gIter->data;
offset++;
if (start_index < stop_index) {
/* stopped */
continue;
} else if (offset < start_index) {
/* action occurred prior to a start */
continue;
}
is_probe = FALSE;
did_change = FALSE;
task = crm_element_value(rsc_op, XML_LRM_ATTR_TASK);
interval_s = crm_element_value(rsc_op, XML_LRM_ATTR_INTERVAL);
interval = crm_parse_int(interval_s, "0");
if (interval == 0 && safe_str_eq(task, RSC_STATUS)) {
is_probe = TRUE;
}
if (interval > 0 && is_set(data_set->flags, pe_flag_maintenance_mode)) {
CancelXmlOp(rsc, rsc_op, node, "maintenance mode", data_set);
} else if (is_probe || safe_str_eq(task, RSC_START) || interval > 0 || safe_str_eq(task, RSC_MIGRATED)) {
did_change = check_action_definition(rsc, node, rsc_op, data_set);
}
if (did_change && get_failcount(node, rsc, NULL, data_set)) {
char *key = NULL;
action_t *action_clear = NULL;
key = generate_op_key(rsc->id, CRM_OP_CLEAR_FAILCOUNT, 0);
action_clear = custom_action(rsc, key, CRM_OP_CLEAR_FAILCOUNT, node, FALSE, TRUE, data_set);
set_bit_inplace(action_clear->flags, pe_action_runnable);
}
}
g_list_free(sorted_op_list);
}
static GListPtr
find_rsc_list(GListPtr result, resource_t * rsc, const char *id, gboolean renamed_clones,
gboolean partial, pe_working_set_t * data_set)
{
GListPtr gIter = NULL;
gboolean match = FALSE;
if (id == NULL) {
return NULL;
} else if (rsc == NULL && data_set) {
for (gIter = data_set->resources; gIter != NULL; gIter = gIter->next) {
resource_t *child = (resource_t *) gIter->data;
result = find_rsc_list(result, child, id, renamed_clones, partial, NULL);
}
return result;
} else if (rsc == NULL) {
return NULL;
}
if (partial) {
if (strstr(rsc->id, id)) {
match = TRUE;
} else if (rsc->long_name && strstr(rsc->long_name, id)) {
match = TRUE;
} else if (renamed_clones && rsc->clone_name && strstr(rsc->clone_name, id)) {
match = TRUE;
}
} else {
if (strcmp(rsc->id, id) == 0) {
match = TRUE;
} else if (rsc->long_name && strcmp(rsc->long_name, id) == 0) {
match = TRUE;
} else if (renamed_clones && rsc->clone_name && strcmp(rsc->clone_name, id) == 0) {
match = TRUE;
}
}
if (match) {
result = g_list_prepend(result, rsc);
}
if (rsc->children) {
gIter = rsc->children;
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child = (resource_t *) gIter->data;
result = find_rsc_list(result, child, id, renamed_clones, partial, NULL);
}
}
return result;
}
static void
check_actions(pe_working_set_t * data_set)
{
const char *id = NULL;
node_t *node = NULL;
xmlNode *lrm_rscs = NULL;
xmlNode *status = get_object_root(XML_CIB_TAG_STATUS, data_set->input);
xmlNode *node_state = NULL;
for (node_state = __xml_first_child(status); node_state != NULL;
node_state = __xml_next(node_state)) {
if (crm_str_eq((const char *)node_state->name, XML_CIB_TAG_STATE, TRUE)) {
id = crm_element_value(node_state, XML_ATTR_ID);
lrm_rscs = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE);
lrm_rscs = find_xml_node(lrm_rscs, XML_LRM_TAG_RESOURCES, FALSE);
node = pe_find_node_id(data_set->nodes, id);
if (node == NULL) {
continue;
} else if (can_run_resources(node) == FALSE) {
crm_trace("Skipping param check for %s: cant run resources",
node->details->uname);
continue;
}
crm_trace("Processing node %s", node->details->uname);
if (node->details->online || is_set(data_set->flags, pe_flag_stonith_enabled)) {
xmlNode *rsc_entry = NULL;
for (rsc_entry = __xml_first_child(lrm_rscs); rsc_entry != NULL;
rsc_entry = __xml_next(rsc_entry)) {
if (crm_str_eq((const char *)rsc_entry->name, XML_LRM_TAG_RESOURCE, TRUE)) {
if (xml_has_children(rsc_entry)) {
GListPtr gIter = NULL;
GListPtr result = NULL;
const char *rsc_id = ID(rsc_entry);
CRM_CHECK(rsc_id != NULL, return);
result = find_rsc_list(NULL, NULL, rsc_id, TRUE, FALSE, data_set);
for (gIter = result; gIter != NULL; gIter = gIter->next) {
resource_t *rsc = (resource_t *) gIter->data;
check_actions_for(rsc_entry, rsc, node, data_set);
}
g_list_free(result);
}
}
}
}
}
}
}
static gboolean
apply_placement_constraints(pe_working_set_t * data_set)
{
GListPtr gIter = NULL;
crm_trace("Applying constraints...");
for (gIter = data_set->placement_constraints; gIter != NULL; gIter = gIter->next) {
rsc_to_node_t *cons = (rsc_to_node_t *) gIter->data;
cons->rsc_lh->cmds->rsc_location(cons->rsc_lh, cons);
}
return TRUE;
}
static void
common_apply_stickiness(resource_t * rsc, node_t * node, pe_working_set_t * data_set)
{
int fail_count = 0;
resource_t *failed = rsc;
if (rsc->children) {
GListPtr gIter = rsc->children;
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t *) gIter->data;
common_apply_stickiness(child_rsc, node, data_set);
}
return;
}
if (is_set(rsc->flags, pe_rsc_managed)
&& rsc->stickiness != 0 && g_list_length(rsc->running_on) == 1) {
node_t *current = pe_find_node_id(rsc->running_on, node->details->id);
node_t *match = pe_hash_table_lookup(rsc->allowed_nodes, node->details->id);
if (current == NULL) {
} else if (match != NULL || is_set(data_set->flags, pe_flag_symmetric_cluster)) {
resource_t *sticky_rsc = rsc;
resource_location(sticky_rsc, node, rsc->stickiness, "stickiness", data_set);
crm_debug("Resource %s: preferring current location"
" (node=%s, weight=%d)", sticky_rsc->id,
node->details->uname, rsc->stickiness);
} else {
GHashTableIter iter;
node_t *nIter = NULL;
crm_debug("Ignoring stickiness for %s: the cluster is asymmetric"
" and node %s is not explicitly allowed", rsc->id, node->details->uname);
g_hash_table_iter_init(&iter, rsc->allowed_nodes);
while (g_hash_table_iter_next(&iter, NULL, (void **)&nIter)) {
crm_err("%s[%s] = %d", rsc->id, nIter->details->uname, nIter->weight);
}
}
}
if (is_not_set(rsc->flags, pe_rsc_unique)) {
failed = uber_parent(rsc);
}
fail_count = get_failcount(node, rsc, NULL, data_set);
if (fail_count > 0 && rsc->migration_threshold != 0) {
if (rsc->migration_threshold <= fail_count) {
resource_location(failed, node, -INFINITY, "__fail_limit__", data_set);
crm_warn("Forcing %s away from %s after %d failures (max=%d)",
failed->id, node->details->uname, fail_count, rsc->migration_threshold);
} else {
crm_notice("%s can fail %d more times on %s before being forced off",
failed->id, rsc->migration_threshold - fail_count, node->details->uname);
}
}
}
static void
complex_set_cmds(resource_t * rsc)
{
GListPtr gIter = rsc->children;
rsc->cmds = &resource_class_alloc_functions[rsc->variant];
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t *) gIter->data;
complex_set_cmds(child_rsc);
}
}
void
set_alloc_actions(pe_working_set_t * data_set)
{
GListPtr gIter = data_set->resources;
for (; gIter != NULL; gIter = gIter->next) {
resource_t *rsc = (resource_t *) gIter->data;
complex_set_cmds(rsc);
}
}
static void
calculate_system_health(gpointer gKey, gpointer gValue, gpointer user_data)
{
const char *key = (const char *)gKey;
const char *value = (const char *)gValue;
int *system_health = (int *)user_data;
if (!gKey || !gValue || !user_data) {
return;
}
/* Does it start with #health? */
if (0 == strncmp(key, "#health", 7)) {
int score;
/* Convert the value into an integer */
score = char2score(value);
/* Add it to the running total */
*system_health = merge_weights(score, *system_health);
}
}
static gboolean
apply_system_health(pe_working_set_t * data_set)
{
GListPtr gIter = NULL;
const char *health_strategy = pe_pref(data_set->config_hash, "node-health-strategy");
if (health_strategy == NULL || safe_str_eq(health_strategy, "none")) {
/* Prevent any accidental health -> score translation */
node_score_red = 0;
node_score_yellow = 0;
node_score_green = 0;
return TRUE;
} else if (safe_str_eq(health_strategy, "migrate-on-red")) {
/* Resources on nodes which have health values of red are
* weighted away from that node.
*/
node_score_red = -INFINITY;
node_score_yellow = 0;
node_score_green = 0;
} else if (safe_str_eq(health_strategy, "only-green")) {
/* Resources on nodes which have health values of red or yellow
* are forced away from that node.
*/
node_score_red = -INFINITY;
node_score_yellow = -INFINITY;
node_score_green = 0;
} else if (safe_str_eq(health_strategy, "progressive")) {
/* Same as the above, but use the r/y/g scores provided by the user
* Defaults are provided by the pe_prefs table
*/
} else if (safe_str_eq(health_strategy, "custom")) {
/* Requires the admin to configure the rsc_location constaints for
* processing the stored health scores
*/
/* TODO: Check for the existance of appropriate node health constraints */
return TRUE;
} else {
crm_err("Unknown node health strategy: %s", health_strategy);
return FALSE;
}
crm_info("Applying automated node health strategy: %s", health_strategy);
for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
int system_health = 0;
node_t *node = (node_t *) gIter->data;
/* Search through the node hash table for system health entries. */
g_hash_table_foreach(node->details->attrs, calculate_system_health, &system_health);
crm_info(" Node %s has an combined system health of %d",
node->details->uname, system_health);
/* If the health is non-zero, then create a new rsc2node so that the
* weight will be added later on.
*/
if (system_health != 0) {
GListPtr gIter2 = data_set->resources;
for (; gIter2 != NULL; gIter2 = gIter2->next) {
resource_t *rsc = (resource_t *) gIter2->data;
rsc2node_new(health_strategy, rsc, system_health, node, data_set);
}
}
}
return TRUE;
}
gboolean
stage0(pe_working_set_t * data_set)
{
xmlNode *cib_constraints = get_object_root(XML_CIB_TAG_CONSTRAINTS, data_set->input);
if (data_set->input == NULL) {
return FALSE;
}
if (is_set(data_set->flags, pe_flag_have_status) == FALSE) {
crm_trace("Calculating status");
cluster_status(data_set);
}
set_alloc_actions(data_set);
apply_system_health(data_set);
unpack_constraints(cib_constraints, data_set);
return TRUE;
}
static void
wait_for_probe(resource_t * rsc, const char *action, action_t * probe_complete,
pe_working_set_t * data_set)
{
if (probe_complete == NULL) {
return;
}
if (rsc->children) {
GListPtr gIter = rsc->children;
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child = (resource_t *) gIter->data;
wait_for_probe(child, action, probe_complete, data_set);
}
} else {
char *key = generate_op_key(rsc->id, action, 0);
custom_action_order(NULL, NULL, probe_complete, rsc, key, NULL,
pe_order_optional, data_set);
}
}
/*
* Check nodes for resources started outside of the LRM
*/
gboolean
probe_resources(pe_working_set_t * data_set)
{
action_t *probe_complete = NULL;
action_t *probe_node_complete = NULL;
GListPtr gIter = NULL;
GListPtr gIter2 = NULL;
gIter = data_set->nodes;
for (; gIter != NULL; gIter = gIter->next) {
node_t *node = (node_t *) gIter->data;
const char *probed = g_hash_table_lookup(node->details->attrs, CRM_OP_PROBED);
if (node->details->online == FALSE) {
continue;
} else if (node->details->unclean) {
continue;
} else if (probe_complete == NULL) {
probe_complete = get_pseudo_op(CRM_OP_PROBED, data_set);
}
if (probed != NULL && crm_is_true(probed) == FALSE) {
action_t *probe_op = custom_action(NULL, crm_strdup(CRM_OP_REPROBE),
CRM_OP_REPROBE, node, FALSE, TRUE, data_set);
add_hash_param(probe_op->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE);
continue;
}
probe_node_complete = custom_action(NULL, crm_strdup(CRM_OP_PROBED),
CRM_OP_PROBED, node, FALSE, TRUE, data_set);
if (crm_is_true(probed)) {
crm_trace("unset");
update_action_flags(probe_node_complete, pe_action_optional);
} else {
crm_trace("set");
update_action_flags(probe_node_complete, pe_action_optional | pe_action_clear);
}
crm_trace("%s - %d", node->details->uname, probe_node_complete->flags & pe_action_optional);
probe_node_complete->priority = INFINITY;
add_hash_param(probe_node_complete->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE);
if (node->details->pending) {
update_action_flags(probe_node_complete, pe_action_runnable | pe_action_clear);
crm_info("Action %s on %s is unrunnable (pending)",
probe_node_complete->uuid, probe_node_complete->node->details->uname);
}
order_actions(probe_node_complete, probe_complete,
pe_order_runnable_left /*|pe_order_implies_then */ );
gIter2 = data_set->resources;
for (; gIter2 != NULL; gIter2 = gIter2->next) {
resource_t *rsc = (resource_t *) gIter2->data;
if (rsc->cmds->create_probe(rsc, node, probe_node_complete, FALSE, data_set)) {
update_action_flags(probe_complete, pe_action_optional | pe_action_clear);
update_action_flags(probe_node_complete, pe_action_optional | pe_action_clear);
wait_for_probe(rsc, RSC_START, probe_complete, data_set);
}
}
}
gIter = data_set->resources;
for (; gIter != NULL; gIter = gIter->next) {
resource_t *rsc = (resource_t *) gIter->data;
wait_for_probe(rsc, RSC_STOP, probe_complete, data_set);
}
return TRUE;
}
/*
* Count how many valid nodes we have (so we know the maximum number of
* colors we can resolve).
*
* Apply node constraints (ie. filter the "allowed_nodes" part of resources
*/
gboolean
stage2(pe_working_set_t * data_set)
{
GListPtr gIter = NULL;
crm_trace("Applying placement constraints");
gIter = data_set->nodes;
for (; gIter != NULL; gIter = gIter->next) {
node_t *node = (node_t *) gIter->data;
if (node == NULL) {
/* error */
} else if (node->weight >= 0.0 /* global weight */
&& node->details->online && node->details->type == node_member) {
data_set->max_valid_nodes++;
}
}
apply_placement_constraints(data_set);
gIter = data_set->nodes;
for (; gIter != NULL; gIter = gIter->next) {
GListPtr gIter2 = NULL;
node_t *node = (node_t *) gIter->data;
gIter2 = data_set->resources;
for (; gIter2 != NULL; gIter2 = gIter2->next) {
resource_t *rsc = (resource_t *) gIter2->data;
common_apply_stickiness(rsc, node, data_set);
}
}
return TRUE;
}
/*
* Create internal resource constraints before allocation
*/
gboolean
stage3(pe_working_set_t * data_set)
{
GListPtr gIter = data_set->resources;
for (; gIter != NULL; gIter = gIter->next) {
resource_t *rsc = (resource_t *) gIter->data;
rsc->cmds->internal_constraints(rsc, data_set);
}
return TRUE;
}
/*
* Check for orphaned or redefined actions
*/
gboolean
stage4(pe_working_set_t * data_set)
{
check_actions(data_set);
return TRUE;
}
static gint
sort_rsc_process_order(gconstpointer a, gconstpointer b, gpointer data)
{
int rc = 0;
int r1_weight = -INFINITY;
int r2_weight = -INFINITY;
const char *reason = "existance";
const GListPtr nodes = (GListPtr) data;
resource_t *resource1 = (resource_t *) convert_const_pointer(a);
resource_t *resource2 = (resource_t *) convert_const_pointer(b);
node_t *node = NULL;
GListPtr gIter = NULL;
GHashTable *r1_nodes = NULL;
GHashTable *r2_nodes = NULL;
if (a == NULL && b == NULL) {
goto done;
}
if (a == NULL) {
return 1;
}
if (b == NULL) {
return -1;
}
reason = "priority";
r1_weight = resource1->priority;
r2_weight = resource2->priority;
if (r1_weight > r2_weight) {
rc = -1;
goto done;
}
if (r1_weight < r2_weight) {
rc = 1;
goto done;
}
reason = "no node list";
if (nodes == NULL) {
goto done;
}
r1_nodes =
rsc_merge_weights(resource1, resource1->id, NULL, NULL, 1,
pe_weights_forward | pe_weights_init);
dump_node_scores(LOG_TRACE, NULL, resource1->id, r1_nodes);
r2_nodes =
rsc_merge_weights(resource2, resource2->id, NULL, NULL, 1,
pe_weights_forward | pe_weights_init);
dump_node_scores(LOG_TRACE, NULL, resource2->id, r2_nodes);
/* Current location score */
reason = "current location";
r1_weight = -INFINITY;
r2_weight = -INFINITY;
if (resource1->running_on) {
node = g_list_nth_data(resource1->running_on, 0);
node = g_hash_table_lookup(r1_nodes, node->details->id);
r1_weight = node->weight;
}
if (resource2->running_on) {
node = g_list_nth_data(resource2->running_on, 0);
node = g_hash_table_lookup(r2_nodes, node->details->id);
r2_weight = node->weight;
}
if (r1_weight > r2_weight) {
rc = -1;
goto done;
}
if (r1_weight < r2_weight) {
rc = 1;
goto done;
}
reason = "score";
for (gIter = nodes; gIter != NULL; gIter = gIter->next) {
node_t *r1_node = NULL;
node_t *r2_node = NULL;
node = (node_t *) gIter->data;
r1_weight = -INFINITY;
if (r1_nodes) {
r1_node = g_hash_table_lookup(r1_nodes, node->details->id);
}
if (r1_node) {
r1_weight = r1_node->weight;
}
r2_weight = -INFINITY;
if (r2_nodes) {
r2_node = g_hash_table_lookup(r2_nodes, node->details->id);
}
if (r2_node) {
r2_weight = r2_node->weight;
}
if (r1_weight > r2_weight) {
rc = -1;
goto done;
}
if (r1_weight < r2_weight) {
rc = 1;
goto done;
}
}
done:
if (r1_nodes) {
g_hash_table_destroy(r1_nodes);
}
if (r2_nodes) {
g_hash_table_destroy(r2_nodes);
}
crm_trace( "%s (%d) %c %s (%d) on %s: %s",
resource1->id, r1_weight, rc < 0 ? '>' : rc > 0 ? '<' : '=',
resource2->id, r2_weight, node ? node->details->id : "n/a", reason);
return rc;
}
gboolean
stage5(pe_working_set_t * data_set)
{
GListPtr gIter = NULL;
if (safe_str_neq(data_set->placement_strategy, "default")) {
GListPtr nodes = g_list_copy(data_set->nodes);
nodes = g_list_sort_with_data(nodes, sort_node_weight, NULL);
data_set->resources =
g_list_sort_with_data(data_set->resources, sort_rsc_process_order, nodes);
g_list_free(nodes);
}
gIter = data_set->nodes;
for (; gIter != NULL; gIter = gIter->next) {
node_t *node = (node_t *) gIter->data;
dump_node_capacity(show_utilization ? 0 : utilization_log_level, "Original", node);
}
crm_trace("Allocating services");
/* Take (next) highest resource, assign it and create its actions */
gIter = data_set->resources;
for (; gIter != NULL; gIter = gIter->next) {
resource_t *rsc = (resource_t *) gIter->data;
crm_trace("Allocating: %s", rsc->id);
rsc->cmds->allocate(rsc, NULL, data_set);
}
gIter = data_set->nodes;
for (; gIter != NULL; gIter = gIter->next) {
node_t *node = (node_t *) gIter->data;
dump_node_capacity(show_utilization ? 0 : utilization_log_level, "Remaining", node);
}
if (is_set(data_set->flags, pe_flag_startup_probes)) {
crm_trace("Calculating needed probes");
/* This code probably needs optimization
* ptest -x with 100 nodes, 100 clones and clone-max=100:
With probes:
ptest[14781]: 2010/09/27_17:56:46 notice: TRACE: do_calculations: pengine.c:258 Calculate cluster status
ptest[14781]: 2010/09/27_17:56:46 notice: TRACE: do_calculations: pengine.c:278 Applying placement constraints
ptest[14781]: 2010/09/27_17:56:47 notice: TRACE: do_calculations: pengine.c:285 Create internal constraints
ptest[14781]: 2010/09/27_17:56:47 notice: TRACE: do_calculations: pengine.c:292 Check actions
ptest[14781]: 2010/09/27_17:56:48 notice: TRACE: do_calculations: pengine.c:299 Allocate resources
ptest[14781]: 2010/09/27_17:56:48 notice: TRACE: stage5: allocate.c:881 Allocating services
ptest[14781]: 2010/09/27_17:56:49 notice: TRACE: stage5: allocate.c:894 Calculating needed probes
ptest[14781]: 2010/09/27_17:56:51 notice: TRACE: stage5: allocate.c:899 Creating actions
ptest[14781]: 2010/09/27_17:56:52 notice: TRACE: stage5: allocate.c:905 Creating done
ptest[14781]: 2010/09/27_17:56:52 notice: TRACE: do_calculations: pengine.c:306 Processing fencing and shutdown cases
ptest[14781]: 2010/09/27_17:56:52 notice: TRACE: do_calculations: pengine.c:313 Applying ordering constraints
36s
ptest[14781]: 2010/09/27_17:57:28 notice: TRACE: do_calculations: pengine.c:320 Create transition graph
Without probes:
ptest[14637]: 2010/09/27_17:56:21 notice: TRACE: do_calculations: pengine.c:258 Calculate cluster status
ptest[14637]: 2010/09/27_17:56:22 notice: TRACE: do_calculations: pengine.c:278 Applying placement constraints
ptest[14637]: 2010/09/27_17:56:22 notice: TRACE: do_calculations: pengine.c:285 Create internal constraints
ptest[14637]: 2010/09/27_17:56:22 notice: TRACE: do_calculations: pengine.c:292 Check actions
ptest[14637]: 2010/09/27_17:56:23 notice: TRACE: do_calculations: pengine.c:299 Allocate resources
ptest[14637]: 2010/09/27_17:56:23 notice: TRACE: stage5: allocate.c:881 Allocating services
ptest[14637]: 2010/09/27_17:56:24 notice: TRACE: stage5: allocate.c:899 Creating actions
ptest[14637]: 2010/09/27_17:56:25 notice: TRACE: stage5: allocate.c:905 Creating done
ptest[14637]: 2010/09/27_17:56:25 notice: TRACE: do_calculations: pengine.c:306 Processing fencing and shutdown cases
ptest[14637]: 2010/09/27_17:56:25 notice: TRACE: do_calculations: pengine.c:313 Applying ordering constraints
ptest[14637]: 2010/09/27_17:56:25 notice: TRACE: do_calculations: pengine.c:320 Create transition graph
*/
probe_resources(data_set);
}
crm_trace("Creating actions");
gIter = data_set->resources;
for (; gIter != NULL; gIter = gIter->next) {
resource_t *rsc = (resource_t *) gIter->data;
rsc->cmds->create_actions(rsc, data_set);
}
crm_trace("Creating done");
return TRUE;
}
static gboolean
is_managed(const resource_t * rsc)
{
GListPtr gIter = rsc->children;
if (is_set(rsc->flags, pe_rsc_managed)) {
return TRUE;
}
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t *) gIter->data;
if (is_managed(child_rsc)) {
return TRUE;
}
}
return FALSE;
}
static gboolean
any_managed_resouces(pe_working_set_t * data_set)
{
GListPtr gIter = data_set->resources;
for (; gIter != NULL; gIter = gIter->next) {
resource_t *rsc = (resource_t *) gIter->data;
if (is_managed(rsc)) {
return TRUE;
}
}
return FALSE;
}
/*
* Create dependancies for stonith and shutdown operations
*/
gboolean
stage6(pe_working_set_t * data_set)
{
action_t *dc_down = NULL;
action_t *dc_fence = NULL;
action_t *stonith_op = NULL;
action_t *last_stonith = NULL;
gboolean integrity_lost = FALSE;
action_t *ready = get_pseudo_op(STONITH_UP, data_set);
action_t *all_stopped = get_pseudo_op(ALL_STOPPED, data_set);
action_t *done = get_pseudo_op(STONITH_DONE, data_set);
gboolean need_stonith = FALSE;
GListPtr gIter = data_set->nodes;
crm_trace("Processing fencing and shutdown cases");
if (is_set(data_set->flags, pe_flag_stonith_enabled)
&& (is_set(data_set->flags, pe_flag_have_quorum)
|| data_set->no_quorum_policy == no_quorum_ignore
|| data_set->no_quorum_policy == no_quorum_suicide)) {
need_stonith = TRUE;
}
if (need_stonith && any_managed_resouces(data_set) == FALSE) {
crm_notice("Delaying fencing operations until there are resources to manage");
need_stonith = FALSE;
}
for (; gIter != NULL; gIter = gIter->next) {
node_t *node = (node_t *) gIter->data;
stonith_op = NULL;
if (node->details->unclean && need_stonith) {
pe_warn("Scheduling Node %s for STONITH", node->details->uname);
stonith_op = custom_action(NULL, crm_strdup(CRM_OP_FENCE),
CRM_OP_FENCE, node, FALSE, TRUE, data_set);
add_hash_param(stonith_op->meta, XML_LRM_ATTR_TARGET, node->details->uname);
add_hash_param(stonith_op->meta, XML_LRM_ATTR_TARGET_UUID, node->details->id);
add_hash_param(stonith_op->meta, "stonith_action", data_set->stonith_action);
stonith_constraints(node, stonith_op, data_set);
order_actions(ready, stonith_op, pe_order_runnable_left);
order_actions(stonith_op, all_stopped, pe_order_implies_then);
clear_bit_inplace(ready->flags, pe_action_optional);
if (node->details->is_dc) {
dc_down = stonith_op;
dc_fence = stonith_op;
} else {
if (last_stonith) {
order_actions(last_stonith, stonith_op, pe_order_optional);
}
last_stonith = stonith_op;
}
} else if (node->details->online && node->details->shutdown) {
action_t *down_op = NULL;
crm_notice("Scheduling Node %s for shutdown", node->details->uname);
down_op = custom_action(NULL, crm_strdup(CRM_OP_SHUTDOWN),
CRM_OP_SHUTDOWN, node, FALSE, TRUE, data_set);
shutdown_constraints(node, down_op, data_set);
add_hash_param(down_op->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE);
if (node->details->is_dc) {
dc_down = down_op;
}
}
if (node->details->unclean && stonith_op == NULL) {
integrity_lost = TRUE;
pe_warn("Node %s is unclean!", node->details->uname);
}
}
if (integrity_lost) {
if (is_set(data_set->flags, pe_flag_stonith_enabled) == FALSE) {
pe_warn("YOUR RESOURCES ARE NOW LIKELY COMPROMISED");
pe_err("ENABLE STONITH TO KEEP YOUR RESOURCES SAFE");
} else if (is_set(data_set->flags, pe_flag_have_quorum) == FALSE) {
crm_notice("Cannot fence unclean nodes until quorum is"
" attained (or no-quorum-policy is set to ignore)");
}
}
if (dc_down != NULL) {
GListPtr shutdown_matches = find_actions(data_set->actions, CRM_OP_SHUTDOWN, NULL);
crm_trace("Ordering shutdowns before %s on %s (DC)",
dc_down->task, dc_down->node->details->uname);
add_hash_param(dc_down->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE);
gIter = shutdown_matches;
for (; gIter != NULL; gIter = gIter->next) {
action_t *node_stop = (action_t *) gIter->data;
if (node_stop->node->details->is_dc) {
continue;
}
crm_debug("Ordering shutdown on %s before %s on %s",
node_stop->node->details->uname,
dc_down->task, dc_down->node->details->uname);
order_actions(node_stop, dc_down, pe_order_optional);
}
if (last_stonith && dc_down != last_stonith) {
order_actions(last_stonith, dc_down, pe_order_optional);
}
g_list_free(shutdown_matches);
}
if (last_stonith) {
order_actions(last_stonith, done, pe_order_implies_then);
} else if (dc_fence) {
order_actions(dc_down, done, pe_order_implies_then);
}
order_actions(ready, done, pe_order_optional);
return TRUE;
}
/*
* Determin the sets of independant actions and the correct order for the
* actions in each set.
*
* Mark dependencies of un-runnable actions un-runnable
*
*/
static GListPtr
find_actions_by_task(GListPtr actions, resource_t * rsc, const char *original_key)
{
GListPtr list = NULL;
list = find_actions(actions, original_key, NULL);
if (list == NULL) {
/* we're potentially searching a child of the original resource */
char *key = NULL;
char *tmp = NULL;
char *task = NULL;
int interval = 0;
if (parse_op_key(original_key, &tmp, &task, &interval)) {
key = generate_op_key(rsc->id, task, interval);
/* crm_err("looking up %s instead of %s", key, original_key); */
/* slist_iter(action, action_t, actions, lpc, */
/* crm_err(" - %s", action->uuid)); */
list = find_actions(actions, key, NULL);
} else {
crm_err("search key: %s", original_key);
}
free(key);
free(tmp);
free(task);
}
return list;
}
static void
rsc_order_then(action_t * lh_action, resource_t * rsc, order_constraint_t * order)
{
GListPtr gIter = NULL;
GListPtr rh_actions = NULL;
action_t *rh_action = NULL;
enum pe_ordering type = order->type;
CRM_CHECK(rsc != NULL, return);
CRM_CHECK(order != NULL, return);
rh_action = order->rh_action;
crm_trace("Processing RH of ordering constraint %d", order->id);
if (rh_action != NULL) {
rh_actions = g_list_prepend(NULL, rh_action);
} else if (rsc != NULL) {
rh_actions = find_actions_by_task(rsc->actions, rsc, order->rh_action_task);
}
if (rh_actions == NULL) {
crm_trace("No RH-Side (%s/%s) found for constraint..."
" ignoring", rsc->id, order->rh_action_task);
if (lh_action) {
crm_trace("LH-Side was: %s", lh_action->uuid);
}
return;
}
if (lh_action && lh_action->rsc == rsc && is_set(lh_action->flags, pe_action_dangle)) {
crm_trace("Detected dangling operation %s -> %s", lh_action->uuid, order->rh_action_task);
clear_bit_inplace(type, pe_order_implies_then);
}
gIter = rh_actions;
for (; gIter != NULL; gIter = gIter->next) {
action_t *rh_action_iter = (action_t *) gIter->data;
if (lh_action) {
order_actions(lh_action, rh_action_iter, type);
} else if (type & pe_order_implies_then) {
update_action_flags(rh_action_iter, pe_action_runnable | pe_action_clear);
crm_warn("Unrunnable %s 0x%.6x", rh_action_iter->uuid, type);
} else {
crm_warn("neither %s 0x%.6x", rh_action_iter->uuid, type);
}
}
g_list_free(rh_actions);
}
static void
rsc_order_first(resource_t * lh_rsc, order_constraint_t * order, pe_working_set_t * data_set)
{
GListPtr gIter = NULL;
GListPtr lh_actions = NULL;
action_t *lh_action = order->lh_action;
resource_t *rh_rsc = order->rh_rsc;
crm_trace("Processing LH of ordering constraint %d", order->id);
CRM_ASSERT(lh_rsc != NULL);
if (lh_action != NULL) {
lh_actions = g_list_prepend(NULL, lh_action);
} else if (lh_action == NULL) {
lh_actions = find_actions_by_task(lh_rsc->actions, lh_rsc, order->lh_action_task);
}
if (lh_actions == NULL && lh_rsc != rh_rsc) {
char *key = NULL;
char *rsc_id = NULL;
char *op_type = NULL;
int interval = 0;
parse_op_key(order->lh_action_task, &rsc_id, &op_type, &interval);
key = generate_op_key(lh_rsc->id, op_type, interval);
if (lh_rsc->fns->state(lh_rsc, TRUE) != RSC_ROLE_STOPPED || safe_str_neq(op_type, RSC_STOP)) {
crm_trace("No LH-Side (%s/%s) found for constraint %d with %s - creating",
lh_rsc->id, order->lh_action_task, order->id, order->rh_action_task);
lh_action = custom_action(lh_rsc, key, op_type, NULL, TRUE, TRUE, data_set);
lh_actions = g_list_prepend(NULL, lh_action);
} else {
free(key);
crm_trace("No LH-Side (%s/%s) found for constraint %d with %s - ignoring",
lh_rsc->id, order->lh_action_task, order->id, order->rh_action_task);
}
free(op_type);
free(rsc_id);
}
gIter = lh_actions;
for (; gIter != NULL; gIter = gIter->next) {
action_t *lh_action_iter = (action_t *) gIter->data;
if (rh_rsc == NULL && order->rh_action) {
rh_rsc = order->rh_action->rsc;
}
if (rh_rsc) {
rsc_order_then(lh_action_iter, rh_rsc, order);
} else if (order->rh_action) {
order_actions(lh_action_iter, order->rh_action, order->type);
}
}
g_list_free(lh_actions);
}
extern gboolean update_action(action_t * action);
gboolean
stage7(pe_working_set_t * data_set)
{
GListPtr gIter = NULL;
crm_trace("Applying ordering constraints");
/* Don't ask me why, but apparently they need to be processed in
* the order they were created in... go figure
*
* Also g_list_prepend() has horrendous performance characteristics
* So we need to use g_list_prepend() and then reverse the list here
*/
data_set->ordering_constraints = g_list_reverse(data_set->ordering_constraints);
gIter = data_set->ordering_constraints;
for (; gIter != NULL; gIter = gIter->next) {
order_constraint_t *order = (order_constraint_t *) gIter->data;
resource_t *rsc = order->lh_rsc;
crm_trace("Applying ordering constraint: %d", order->id);
if (rsc != NULL) {
crm_trace("rsc_action-to-*");
rsc_order_first(rsc, order, data_set);
continue;
}
rsc = order->rh_rsc;
if (rsc != NULL) {
crm_trace("action-to-rsc_action");
rsc_order_then(order->lh_action, rsc, order);
} else {
crm_trace("action-to-action");
order_actions(order->lh_action, order->rh_action, order->type);
}
}
crm_trace("Updating %d actions", g_list_length(data_set->actions));
gIter = data_set->actions;
for (; gIter != NULL; gIter = gIter->next) {
action_t *action = (action_t *) gIter->data;
update_action(action);
}
crm_trace("Processing migrations");
gIter = data_set->resources;
for (; gIter != NULL; gIter = gIter->next) {
resource_t *rsc = (resource_t *) gIter->data;
rsc_migrate_reload(rsc, data_set);
LogActions(rsc, data_set, FALSE);
}
return TRUE;
}
static gint
sort_notify_entries(gconstpointer a, gconstpointer b)
{
int tmp;
const notify_entry_t *entry_a = a;
const notify_entry_t *entry_b = b;
if (entry_a == NULL && entry_b == NULL) {
return 0;
}
if (entry_a == NULL) {
return 1;
}
if (entry_b == NULL) {
return -1;
}
if (entry_a->rsc == NULL && entry_b->rsc == NULL) {
return 0;
}
if (entry_a->rsc == NULL) {
return 1;
}
if (entry_b->rsc == NULL) {
return -1;
}
tmp = strcmp(entry_a->rsc->id, entry_b->rsc->id);
if (tmp != 0) {
return tmp;
}
if (entry_a->node == NULL && entry_b->node == NULL) {
return 0;
}
if (entry_a->node == NULL) {
return 1;
}
if (entry_b->node == NULL) {
return -1;
}
return strcmp(entry_a->node->details->id, entry_b->node->details->id);
}
static void
expand_list(GListPtr list, char **rsc_list, char **node_list)
{
GListPtr gIter = list;
const char *uname = NULL;
const char *rsc_id = NULL;
const char *last_rsc_id = NULL;
if (rsc_list) {
*rsc_list = NULL;
}
if (list == NULL) {
if (rsc_list) {
*rsc_list = crm_strdup(" ");
}
if (node_list) {
*node_list = crm_strdup(" ");
}
return;
}
if (node_list) {
*node_list = NULL;
}
for (; gIter != NULL; gIter = gIter->next) {
notify_entry_t *entry = (notify_entry_t *) gIter->data;
CRM_CHECK(entry != NULL, continue);
CRM_CHECK(entry->rsc != NULL, continue);
CRM_CHECK(node_list == NULL || entry->node != NULL, continue);
uname = NULL;
rsc_id = entry->rsc->id;
CRM_ASSERT(rsc_id != NULL);
/* filter dups */
if (safe_str_eq(rsc_id, last_rsc_id)) {
continue;
}
last_rsc_id = rsc_id;
if (rsc_list != NULL) {
int existing_len = 0;
int len = 2 + strlen(rsc_id); /* +1 space, +1 EOS */
if (rsc_list && *rsc_list) {
existing_len = strlen(*rsc_list);
}
crm_trace("Adding %s (%dc) at offset %d", rsc_id, len - 2, existing_len);
crm_realloc(*rsc_list, len + existing_len);
sprintf(*rsc_list + existing_len, "%s ", rsc_id);
}
if (entry->node != NULL) {
uname = entry->node->details->uname;
}
if (node_list != NULL && uname) {
int existing_len = 0;
int len = 2 + strlen(uname);
if (node_list && *node_list) {
existing_len = strlen(*node_list);
}
crm_trace("Adding %s (%dc) at offset %d", uname, len - 2, existing_len);
crm_realloc(*node_list, len + existing_len);
sprintf(*node_list + existing_len, "%s ", uname);
}
}
}
static void
dup_attr(gpointer key, gpointer value, gpointer user_data)
{
add_hash_param(user_data, key, value);
}
static action_t *
pe_notify(resource_t * rsc, node_t * node, action_t * op, action_t * confirm,
notify_data_t * n_data, pe_working_set_t * data_set)
{
char *key = NULL;
action_t *trigger = NULL;
const char *value = NULL;
const char *task = NULL;
if (op == NULL || confirm == NULL) {
crm_trace("Op=%p confirm=%p", op, confirm);
return NULL;
}
CRM_CHECK(node != NULL, return NULL);
if (node->details->online == FALSE) {
crm_trace("Skipping notification for %s: node offline", rsc->id);
return NULL;
} else if (is_set(op->flags, pe_action_runnable) == FALSE) {
crm_trace("Skipping notification for %s: not runnable", op->uuid);
return NULL;
}
value = g_hash_table_lookup(op->meta, "notify_type");
task = g_hash_table_lookup(op->meta, "notify_operation");
crm_trace("Creating notify actions for %s: %s (%s-%s)", op->uuid, rsc->id, value, task);
key = generate_notify_key(rsc->id, value, task);
trigger = custom_action(rsc, key, op->task, node,
is_set(op->flags, pe_action_optional), TRUE, data_set);
g_hash_table_foreach(op->meta, dup_attr, trigger->meta);
g_hash_table_foreach(n_data->keys, dup_attr, trigger->meta);
/* pseudo_notify before notify */
crm_trace("Ordering %s before %s (%d->%d)", op->uuid, trigger->uuid, trigger->id, op->id);
order_actions(op, trigger, pe_order_optional);
order_actions(trigger, confirm, pe_order_optional);
return trigger;
}
static void
pe_post_notify(resource_t * rsc, node_t * node, notify_data_t * n_data, pe_working_set_t * data_set)
{
action_t *notify = NULL;
CRM_CHECK(rsc != NULL, return);
if (n_data->post == NULL) {
return; /* Nothing to do */
}
notify = pe_notify(rsc, node, n_data->post, n_data->post_done, n_data, data_set);
if (notify != NULL) {
notify->priority = INFINITY;
}
if (n_data->post_done) {
GListPtr gIter = rsc->actions;
for (; gIter != NULL; gIter = gIter->next) {
action_t *mon = (action_t *) gIter->data;
const char *interval = g_hash_table_lookup(mon->meta, "interval");
if (interval == NULL || safe_str_eq(interval, "0")) {
crm_trace("Skipping %s: interval", mon->uuid);
continue;
} else if (safe_str_eq(mon->task, "cancel")) {
crm_trace("Skipping %s: cancel", mon->uuid);
continue;
}
order_actions(n_data->post_done, mon, pe_order_optional);
}
}
}
notify_data_t *
create_notification_boundaries(resource_t * rsc, const char *action, action_t * start,
action_t * end, pe_working_set_t * data_set)
{
/* Create the pseudo ops that preceed and follow the actual notifications */
/*
* Creates two sequences (conditional on start and end being supplied):
* pre_notify -> pre_notify_complete -> start, and
* end -> post_notify -> post_notify_complete
*
* 'start' and 'end' may be the same event or ${X} and ${X}ed as per clones
*/
char *key = NULL;
notify_data_t *n_data = NULL;
if (is_not_set(rsc->flags, pe_rsc_notify)) {
return NULL;
}
n_data = calloc(1, sizeof(notify_data_t));
n_data->action = action;
n_data->keys =
g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str);
if (start) {
/* create pre-event notification wrappers */
key = generate_notify_key(rsc->id, "pre", start->task);
n_data->pre =
custom_action(rsc, key, RSC_NOTIFY, NULL, is_set(start->flags, pe_action_optional),
TRUE, data_set);
update_action_flags(n_data->pre, pe_action_pseudo);
update_action_flags(n_data->pre, pe_action_runnable);
add_hash_param(n_data->pre->meta, "notify_type", "pre");
add_hash_param(n_data->pre->meta, "notify_operation", n_data->action);
/* create pre_notify_complete */
key = generate_notify_key(rsc->id, "confirmed-pre", start->task);
n_data->pre_done =
custom_action(rsc, key, RSC_NOTIFIED, NULL, is_set(start->flags, pe_action_optional),
TRUE, data_set);
update_action_flags(n_data->pre_done, pe_action_pseudo);
update_action_flags(n_data->pre_done, pe_action_runnable);
add_hash_param(n_data->pre_done->meta, "notify_type", "pre");
add_hash_param(n_data->pre_done->meta, "notify_operation", n_data->action);
order_actions(n_data->pre_done, start, pe_order_optional);
order_actions(n_data->pre, n_data->pre_done, pe_order_optional);
}
if (end) {
/* create post-event notification wrappers */
key = generate_notify_key(rsc->id, "post", end->task);
n_data->post =
custom_action(rsc, key, RSC_NOTIFY, NULL, is_set(end->flags, pe_action_optional), TRUE,
data_set);
n_data->post->priority = INFINITY;
update_action_flags(n_data->post, pe_action_pseudo);
if (is_set(end->flags, pe_action_runnable)) {
update_action_flags(n_data->post, pe_action_runnable);
} else {
update_action_flags(n_data->post, pe_action_runnable | pe_action_clear);
}
add_hash_param(n_data->post->meta, "notify_type", "post");
add_hash_param(n_data->post->meta, "notify_operation", n_data->action);
/* create post_notify_complete */
key = generate_notify_key(rsc->id, "confirmed-post", end->task);
n_data->post_done =
custom_action(rsc, key, RSC_NOTIFIED, NULL, is_set(end->flags, pe_action_optional),
TRUE, data_set);
n_data->post_done->priority = INFINITY;
update_action_flags(n_data->post_done, pe_action_pseudo);
if (is_set(end->flags, pe_action_runnable)) {
update_action_flags(n_data->post_done, pe_action_runnable);
} else {
update_action_flags(n_data->post_done, pe_action_runnable | pe_action_clear);
}
add_hash_param(n_data->post_done->meta, "notify_type", "pre");
add_hash_param(n_data->post_done->meta, "notify_operation", n_data->action);
order_actions(end, n_data->post, pe_order_implies_then);
order_actions(n_data->post, n_data->post_done, pe_order_implies_then);
}
if (start && end) {
order_actions(n_data->pre_done, n_data->post, pe_order_optional);
}
if (safe_str_eq(action, RSC_STOP)) {
action_t *all_stopped = get_pseudo_op(ALL_STOPPED, data_set);
order_actions(n_data->post_done, all_stopped, pe_order_optional);
}
return n_data;
}
void
collect_notification_data(resource_t * rsc, gboolean state, gboolean activity,
notify_data_t * n_data)
{
if (rsc->children) {
GListPtr gIter = rsc->children;
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child = (resource_t *) gIter->data;
collect_notification_data(child, state, activity, n_data);
}
return;
}
if (state) {
notify_entry_t *entry = NULL;
entry = calloc(1, sizeof(notify_entry_t));
entry->rsc = rsc;
if (rsc->running_on) {
/* we only take the first one */
entry->node = rsc->running_on->data;
}
crm_trace("%s state: %s", rsc->id, role2text(rsc->role));
switch (rsc->role) {
case RSC_ROLE_STOPPED:
n_data->inactive = g_list_prepend(n_data->inactive, entry);
break;
case RSC_ROLE_STARTED:
n_data->active = g_list_prepend(n_data->active, entry);
break;
case RSC_ROLE_SLAVE:
n_data->slave = g_list_prepend(n_data->slave, entry);
break;
case RSC_ROLE_MASTER:
n_data->master = g_list_prepend(n_data->master, entry);
break;
default:
crm_err("Unsupported notify role");
free(entry);
break;
}
}
if (activity) {
notify_entry_t *entry = NULL;
enum action_tasks task;
GListPtr gIter = rsc->actions;
for (; gIter != NULL; gIter = gIter->next) {
action_t *op = (action_t *) gIter->data;
if (is_set(op->flags, pe_action_optional) == FALSE && op->node != NULL) {
entry = calloc(1, sizeof(notify_entry_t));
entry->node = op->node;
entry->rsc = rsc;
task = text2task(op->task);
switch (task) {
case start_rsc:
n_data->start = g_list_prepend(n_data->start, entry);
break;
case stop_rsc:
n_data->stop = g_list_prepend(n_data->stop, entry);
break;
case action_promote:
n_data->promote = g_list_prepend(n_data->promote, entry);
break;
case action_demote:
n_data->demote = g_list_prepend(n_data->demote, entry);
break;
default:
free(entry);
break;
}
}
}
}
}
gboolean
expand_notification_data(notify_data_t * n_data)
{
/* Expand the notification entries into a key=value hashtable
* This hashtable is later used in action2xml()
*/
gboolean required = FALSE;
char *rsc_list = NULL;
char *node_list = NULL;
if (n_data->stop) {
n_data->stop = g_list_sort(n_data->stop, sort_notify_entries);
}
expand_list(n_data->stop, &rsc_list, &node_list);
if (rsc_list != NULL && safe_str_neq(" ", rsc_list)) {
if (safe_str_eq(n_data->action, RSC_STOP)) {
required = TRUE;
}
}
g_hash_table_insert(n_data->keys, crm_strdup("notify_stop_resource"), rsc_list);
g_hash_table_insert(n_data->keys, crm_strdup("notify_stop_uname"), node_list);
if (n_data->start) {
n_data->start = g_list_sort(n_data->start, sort_notify_entries);
if (rsc_list && safe_str_eq(n_data->action, RSC_START)) {
required = TRUE;
}
}
expand_list(n_data->start, &rsc_list, &node_list);
g_hash_table_insert(n_data->keys, crm_strdup("notify_start_resource"), rsc_list);
g_hash_table_insert(n_data->keys, crm_strdup("notify_start_uname"), node_list);
if (n_data->demote) {
n_data->demote = g_list_sort(n_data->demote, sort_notify_entries);
if (safe_str_eq(n_data->action, RSC_DEMOTE)) {
required = TRUE;
}
}
expand_list(n_data->demote, &rsc_list, &node_list);
g_hash_table_insert(n_data->keys, crm_strdup("notify_demote_resource"), rsc_list);
g_hash_table_insert(n_data->keys, crm_strdup("notify_demote_uname"), node_list);
if (n_data->promote) {
n_data->promote = g_list_sort(n_data->promote, sort_notify_entries);
if (safe_str_eq(n_data->action, RSC_PROMOTE)) {
required = TRUE;
}
}
expand_list(n_data->promote, &rsc_list, &node_list);
g_hash_table_insert(n_data->keys, crm_strdup("notify_promote_resource"), rsc_list);
g_hash_table_insert(n_data->keys, crm_strdup("notify_promote_uname"), node_list);
if (n_data->active) {
n_data->active = g_list_sort(n_data->active, sort_notify_entries);
}
expand_list(n_data->active, &rsc_list, &node_list);
g_hash_table_insert(n_data->keys, crm_strdup("notify_active_resource"), rsc_list);
g_hash_table_insert(n_data->keys, crm_strdup("notify_active_uname"), node_list);
if (n_data->slave) {
n_data->slave = g_list_sort(n_data->slave, sort_notify_entries);
}
expand_list(n_data->slave, &rsc_list, &node_list);
g_hash_table_insert(n_data->keys, crm_strdup("notify_slave_resource"), rsc_list);
g_hash_table_insert(n_data->keys, crm_strdup("notify_slave_uname"), node_list);
if (n_data->master) {
n_data->master = g_list_sort(n_data->master, sort_notify_entries);
}
expand_list(n_data->master, &rsc_list, &node_list);
g_hash_table_insert(n_data->keys, crm_strdup("notify_master_resource"), rsc_list);
g_hash_table_insert(n_data->keys, crm_strdup("notify_master_uname"), node_list);
if (n_data->inactive) {
n_data->inactive = g_list_sort(n_data->inactive, sort_notify_entries);
}
expand_list(n_data->inactive, &rsc_list, NULL);
g_hash_table_insert(n_data->keys, crm_strdup("notify_inactive_resource"), rsc_list);
if (required && n_data->pre) {
update_action_flags(n_data->pre, pe_action_optional | pe_action_clear);
update_action_flags(n_data->pre_done, pe_action_optional | pe_action_clear);
}
if (required && n_data->post) {
update_action_flags(n_data->post, pe_action_optional | pe_action_clear);
update_action_flags(n_data->post_done, pe_action_optional | pe_action_clear);
}
return required;
}
void
create_notifications(resource_t * rsc, notify_data_t * n_data, pe_working_set_t * data_set)
{
GListPtr gIter = NULL;
action_t *stop = NULL;
action_t *start = NULL;
enum action_tasks task = text2task(n_data->action);
if (rsc->children) {
gIter = rsc->children;
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child = (resource_t *) gIter->data;
create_notifications(child, n_data, data_set);
}
return;
}
/* Copy notification details into standard ops */
gIter = rsc->actions;
for (; gIter != NULL; gIter = gIter->next) {
action_t *op = (action_t *) gIter->data;
if (is_set(op->flags, pe_action_optional) == FALSE && op->node != NULL) {
enum action_tasks t = text2task(op->task);
switch (t) {
case start_rsc:
case stop_rsc:
case action_promote:
case action_demote:
g_hash_table_foreach(n_data->keys, dup_attr, op->meta);
break;
default:
break;
}
}
}
crm_trace("Creating notificaitons for: %s.%s (%s->%s)",
n_data->action, rsc->id, role2text(rsc->role), role2text(rsc->next_role));
stop = find_first_action(rsc->actions, NULL, RSC_STOP, NULL);
start = find_first_action(rsc->actions, NULL, RSC_START, NULL);
/* stop / demote */
if (rsc->role != RSC_ROLE_STOPPED) {
if (task == stop_rsc || task == action_demote) {
gIter = rsc->running_on;
for (; gIter != NULL; gIter = gIter->next) {
node_t *current_node = (node_t *) gIter->data;
pe_notify(rsc, current_node, n_data->pre, n_data->pre_done, n_data, data_set);
if (task == action_demote || stop == NULL
|| is_set(stop->flags, pe_action_optional)) {
pe_post_notify(rsc, current_node, n_data, data_set);
}
}
}
}
/* start / promote */
if (rsc->next_role != RSC_ROLE_STOPPED) {
if (rsc->allocated_to == NULL) {
pe_proc_err("Next role '%s' but %s is not allocated", role2text(rsc->next_role),
rsc->id);
} else if (task == start_rsc || task == action_promote) {
if (task != start_rsc || start == NULL || is_set(start->flags, pe_action_optional)) {
pe_notify(rsc, rsc->allocated_to, n_data->pre, n_data->pre_done, n_data, data_set);
}
pe_post_notify(rsc, rsc->allocated_to, n_data, data_set);
}
}
}
void
free_notification_data(notify_data_t * n_data)
{
if (n_data == NULL) {
return;
}
- slist_basic_destroy(n_data->stop);
- slist_basic_destroy(n_data->start);
- slist_basic_destroy(n_data->demote);
- slist_basic_destroy(n_data->promote);
- slist_basic_destroy(n_data->master);
- slist_basic_destroy(n_data->slave);
- slist_basic_destroy(n_data->active);
- slist_basic_destroy(n_data->inactive);
+ g_list_free_full(n_data->stop, free);
+ g_list_free_full(n_data->start, free);
+ g_list_free_full(n_data->demote, free);
+ g_list_free_full(n_data->promote, free);
+ g_list_free_full(n_data->master, free);
+ g_list_free_full(n_data->slave, free);
+ g_list_free_full(n_data->active, free);
+ g_list_free_full(n_data->inactive, free);
g_hash_table_destroy(n_data->keys);
free(n_data);
}
int transition_id = -1;
/*
* Create a dependency graph to send to the transitioner (via the CRMd)
*/
gboolean
stage8(pe_working_set_t * data_set)
{
GListPtr gIter = NULL;
const char *value = NULL;
transition_id++;
crm_trace("Creating transition graph %d.", transition_id);
data_set->graph = create_xml_node(NULL, XML_TAG_GRAPH);
value = pe_pref(data_set->config_hash, "cluster-delay");
crm_xml_add(data_set->graph, "cluster-delay", value);
value = pe_pref(data_set->config_hash, "stonith-timeout");
crm_xml_add(data_set->graph, "stonith-timeout", value);
crm_xml_add(data_set->graph, "failed-stop-offset", "INFINITY");
if (is_set(data_set->flags, pe_flag_start_failure_fatal)) {
crm_xml_add(data_set->graph, "failed-start-offset", "INFINITY");
} else {
crm_xml_add(data_set->graph, "failed-start-offset", "1");
}
value = pe_pref(data_set->config_hash, "batch-limit");
crm_xml_add(data_set->graph, "batch-limit", value);
crm_xml_add_int(data_set->graph, "transition_id", transition_id);
value = pe_pref(data_set->config_hash, "migration-limit");
if (crm_int_helper(value, NULL) > 0) {
crm_xml_add(data_set->graph, "migration-limit", value);
}
/* errors...
slist_iter(action, action_t, action_list, lpc,
if(action->optional == FALSE && action->runnable == FALSE) {
print_action("Ignoring", action, TRUE);
}
);
*/
gIter = data_set->resources;
for (; gIter != NULL; gIter = gIter->next) {
resource_t *rsc = (resource_t *) gIter->data;
crm_trace("processing actions for rsc=%s", rsc->id);
rsc->cmds->expand(rsc, data_set);
}
crm_log_xml_trace(data_set->graph, "created resource-driven action list");
/* catch any non-resource specific actions */
crm_trace("processing non-resource actions");
gIter = data_set->actions;
for (; gIter != NULL; gIter = gIter->next) {
action_t *action = (action_t *) gIter->data;
if (action->rsc
&& action->node
&& action->node->details->shutdown
&& is_not_set(data_set->flags, pe_flag_maintenance_mode)
&& is_not_set(action->flags, pe_action_optional)
&& is_not_set(action->flags, pe_action_runnable)
&& crm_str_eq(action->task, RSC_STOP, TRUE)
) {
crm_crit("Cannot shut down node '%s' because of %s:%s%s",
action->node->details->uname, action->rsc->id,
is_not_set(action->rsc->flags, pe_rsc_managed)?" unmanaged":" blocked",
is_set(action->rsc->flags, pe_rsc_failed)?" failed":"");
}
graph_element_from_action(action, data_set);
}
crm_log_xml_trace(data_set->graph, "created generic action list");
crm_trace("Created transition graph %d.", transition_id);
return TRUE;
}
void
cleanup_alloc_calculations(pe_working_set_t * data_set)
{
if (data_set == NULL) {
return;
}
crm_trace("deleting %d order cons: %p",
g_list_length(data_set->ordering_constraints), data_set->ordering_constraints);
pe_free_ordering(data_set->ordering_constraints);
data_set->ordering_constraints = NULL;
crm_trace("deleting %d node cons: %p",
g_list_length(data_set->placement_constraints), data_set->placement_constraints);
pe_free_rsc_to_node(data_set->placement_constraints);
data_set->placement_constraints = NULL;
crm_trace("deleting %d inter-resource cons: %p",
g_list_length(data_set->colocation_constraints), data_set->colocation_constraints);
- slist_basic_destroy(data_set->colocation_constraints);
+ g_list_free_full(data_set->colocation_constraints, free);
data_set->colocation_constraints = NULL;
crm_trace("deleting %d ticket deps: %p",
g_list_length(data_set->ticket_constraints), data_set->ticket_constraints);
- slist_basic_destroy(data_set->ticket_constraints);
+ g_list_free_full(data_set->ticket_constraints, free);
data_set->ticket_constraints = NULL;
cleanup_calculations(data_set);
}
diff --git a/pengine/group.c b/pengine/group.c
index 0a298cb2d9..19e89620a3 100644
--- a/pengine/group.c
+++ b/pengine/group.c
@@ -1,515 +1,515 @@
/*
* 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 <pengine.h>
#include <lib/pengine/utils.h>
#include <crm/msg_xml.h>
#include <allocate.h>
#include <utils.h>
#define VARIANT_GROUP 1
#include <lib/pengine/variant.h>
node_t *
group_color(resource_t * rsc, node_t * prefer, pe_working_set_t * data_set)
{
node_t *node = NULL;
node_t *group_node = NULL;
GListPtr gIter = NULL;
group_variant_data_t *group_data = NULL;
get_group_variant_data(group_data, rsc);
if (is_not_set(rsc->flags, pe_rsc_provisional)) {
return rsc->allocated_to;
}
crm_trace("Processing %s", rsc->id);
if (is_set(rsc->flags, pe_rsc_allocating)) {
crm_debug("Dependency loop detected involving %s", rsc->id);
return NULL;
}
if (group_data->first_child == NULL) {
/* nothign to allocate */
clear_bit(rsc->flags, pe_rsc_provisional);
return NULL;
}
set_bit(rsc->flags, pe_rsc_allocating);
rsc->role = group_data->first_child->role;
group_data->first_child->rsc_cons =
g_list_concat(group_data->first_child->rsc_cons, rsc->rsc_cons);
rsc->rsc_cons = NULL;
group_data->first_child->rsc_cons_lhs =
g_list_concat(group_data->first_child->rsc_cons_lhs, rsc->rsc_cons_lhs);
rsc->rsc_cons_lhs = NULL;
gIter = rsc->rsc_tickets;
for (; gIter != NULL; gIter = gIter->next) {
rsc_ticket_t *rsc_ticket = (rsc_ticket_t *) gIter->data;
if (rsc_ticket->ticket->granted == FALSE || rsc_ticket->ticket->standby) {
rsc_ticket_constraint(rsc, rsc_ticket, data_set);
}
}
dump_node_scores(show_scores ? 0 : scores_log_level, rsc, __PRETTY_FUNCTION__,
rsc->allowed_nodes);
gIter = rsc->children;
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t *) gIter->data;
node = child_rsc->cmds->allocate(child_rsc, prefer, data_set);
if (group_node == NULL) {
group_node = node;
}
}
rsc->next_role = group_data->first_child->next_role;
clear_bit(rsc->flags, pe_rsc_allocating);
clear_bit(rsc->flags, pe_rsc_provisional);
if (group_data->colocated) {
return group_node;
}
return NULL;
}
void group_update_pseudo_status(resource_t * parent, resource_t * child);
void
group_create_actions(resource_t * rsc, pe_working_set_t * data_set)
{
action_t *op = NULL;
const char *value = NULL;
GListPtr gIter = rsc->children;
crm_trace("Creating actions for %s", rsc->id);
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t *) gIter->data;
child_rsc->cmds->create_actions(child_rsc, data_set);
group_update_pseudo_status(rsc, child_rsc);
}
op = start_action(rsc, NULL, TRUE /* !group_data->child_starting */ );
set_bit_inplace(op->flags, pe_action_pseudo | pe_action_runnable);
op = custom_action(rsc, started_key(rsc),
RSC_STARTED, NULL, TRUE /* !group_data->child_starting */ , TRUE, data_set);
set_bit_inplace(op->flags, pe_action_pseudo | pe_action_runnable);
op = stop_action(rsc, NULL, TRUE /* !group_data->child_stopping */ );
set_bit_inplace(op->flags, pe_action_pseudo | pe_action_runnable);
op = custom_action(rsc, stopped_key(rsc),
RSC_STOPPED, NULL, TRUE /* !group_data->child_stopping */ , TRUE, data_set);
set_bit_inplace(op->flags, pe_action_pseudo | pe_action_runnable);
value = g_hash_table_lookup(rsc->meta, "stateful");
if (crm_is_true(value)) {
op = custom_action(rsc, demote_key(rsc), RSC_DEMOTE, NULL, TRUE, TRUE, data_set);
set_bit_inplace(op->flags, pe_action_pseudo);
set_bit_inplace(op->flags, pe_action_runnable);
op = custom_action(rsc, demoted_key(rsc), RSC_DEMOTED, NULL, TRUE, TRUE, data_set);
set_bit_inplace(op->flags, pe_action_pseudo);
set_bit_inplace(op->flags, pe_action_runnable);
op = custom_action(rsc, promote_key(rsc), RSC_PROMOTE, NULL, TRUE, TRUE, data_set);
set_bit_inplace(op->flags, pe_action_pseudo);
set_bit_inplace(op->flags, pe_action_runnable);
op = custom_action(rsc, promoted_key(rsc), RSC_PROMOTED, NULL, TRUE, TRUE, data_set);
set_bit_inplace(op->flags, pe_action_pseudo);
set_bit_inplace(op->flags, pe_action_runnable);
}
}
void
group_update_pseudo_status(resource_t * parent, resource_t * child)
{
GListPtr gIter = child->actions;
group_variant_data_t *group_data = NULL;
get_group_variant_data(group_data, parent);
if (group_data->ordered == FALSE) {
/* If this group is not ordered, then leave the meta-actions as optional */
return;
}
if (group_data->child_stopping && group_data->child_starting) {
return;
}
for (; gIter != NULL; gIter = gIter->next) {
action_t *action = (action_t *) gIter->data;
if (is_set(action->flags, pe_action_optional)) {
continue;
}
if (safe_str_eq(RSC_STOP, action->task) && is_set(action->flags, pe_action_runnable)) {
group_data->child_stopping = TRUE;
crm_trace("Based on %s the group is stopping", action->uuid);
} else if (safe_str_eq(RSC_START, action->task)
&& is_set(action->flags, pe_action_runnable)) {
group_data->child_starting = TRUE;
crm_trace("Based on %s the group is starting", action->uuid);
}
}
}
void
group_internal_constraints(resource_t * rsc, pe_working_set_t * data_set)
{
GListPtr gIter = rsc->children;
resource_t *last_rsc = NULL;
resource_t *top = uber_parent(rsc);
group_variant_data_t *group_data = NULL;
get_group_variant_data(group_data, rsc);
new_rsc_order(rsc, RSC_STOPPED, rsc, RSC_START, pe_order_optional, data_set);
new_rsc_order(rsc, RSC_START, rsc, RSC_STARTED, pe_order_runnable_left, data_set);
new_rsc_order(rsc, RSC_STOP, rsc, RSC_STOPPED, pe_order_runnable_left, data_set);
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t *) gIter->data;
int stop = pe_order_none;
int stopped = pe_order_implies_then_printed;
int start = pe_order_implies_then | pe_order_runnable_left;
int started =
pe_order_runnable_left | pe_order_implies_then | pe_order_implies_then_printed;
child_rsc->cmds->internal_constraints(child_rsc, data_set);
if (last_rsc == NULL) {
if (group_data->ordered) {
stop |= pe_order_optional;
stopped = pe_order_implies_then;
}
} else if (group_data->colocated) {
rsc_colocation_new("group:internal_colocation", NULL, INFINITY,
child_rsc, last_rsc, NULL, NULL, data_set);
}
if (top->variant == pe_master) {
new_rsc_order(rsc, RSC_DEMOTE, child_rsc, RSC_DEMOTE,
stop | pe_order_implies_first_printed, data_set);
new_rsc_order(child_rsc, RSC_DEMOTE, rsc, RSC_DEMOTED, stopped, data_set);
new_rsc_order(child_rsc, RSC_PROMOTE, rsc, RSC_PROMOTED, started, data_set);
new_rsc_order(rsc, RSC_PROMOTE, child_rsc, RSC_PROMOTE,
pe_order_implies_first_printed, data_set);
}
order_start_start(rsc, child_rsc, pe_order_implies_first_printed);
order_stop_stop(rsc, child_rsc, stop | pe_order_implies_first_printed);
new_rsc_order(child_rsc, RSC_STOP, rsc, RSC_STOPPED, stopped, data_set);
new_rsc_order(child_rsc, RSC_START, rsc, RSC_STARTED, started, data_set);
if (group_data->ordered == FALSE) {
order_start_start(rsc, child_rsc, start | pe_order_implies_first_printed);
if (top->variant == pe_master) {
new_rsc_order(rsc, RSC_PROMOTE, child_rsc, RSC_PROMOTE,
start | pe_order_implies_first_printed, data_set);
}
} else if (last_rsc != NULL) {
child_rsc->restart_type = pe_restart_restart;
order_start_start(last_rsc, child_rsc, start);
order_stop_stop(child_rsc, last_rsc, pe_order_optional|pe_order_restart);
if (top->variant == pe_master) {
new_rsc_order(last_rsc, RSC_PROMOTE, child_rsc, RSC_PROMOTE, start, data_set);
new_rsc_order(child_rsc, RSC_DEMOTE, last_rsc, RSC_DEMOTE, pe_order_optional,
data_set);
}
} else {
/* If anyone in the group is starting, then
* pe_order_implies_then will cause _everyone_ in the group
* to be sent a start action
* But this is safe since starting something that is already
* started is required to be "safe"
*/
int flags = pe_order_none;
order_start_start(rsc, child_rsc, flags);
if (top->variant == pe_master) {
new_rsc_order(rsc, RSC_PROMOTE, child_rsc, RSC_PROMOTE, flags, data_set);
}
}
last_rsc = child_rsc;
}
if (group_data->ordered && last_rsc != NULL) {
int stop_stop_flags = pe_order_implies_then;
int stop_stopped_flags = pe_order_optional;
order_stop_stop(rsc, last_rsc, stop_stop_flags);
new_rsc_order(last_rsc, RSC_STOP, rsc, RSC_STOPPED, stop_stopped_flags, data_set);
if (top->variant == pe_master) {
new_rsc_order(rsc, RSC_DEMOTE, last_rsc, RSC_DEMOTE, stop_stop_flags, data_set);
new_rsc_order(last_rsc, RSC_DEMOTE, rsc, RSC_DEMOTED, stop_stopped_flags, data_set);
}
}
}
void
group_rsc_colocation_lh(resource_t * rsc_lh, resource_t * rsc_rh, rsc_colocation_t * constraint)
{
GListPtr gIter = NULL;
group_variant_data_t *group_data = NULL;
if (rsc_lh == NULL) {
pe_err("rsc_lh was NULL for %s", constraint->id);
return;
} else if (rsc_rh == NULL) {
pe_err("rsc_rh was NULL for %s", constraint->id);
return;
}
gIter = rsc_lh->children;
crm_trace("Processing constraints from %s", rsc_lh->id);
get_group_variant_data(group_data, rsc_lh);
if (group_data->colocated) {
group_data->first_child->cmds->rsc_colocation_lh(group_data->first_child, rsc_rh,
constraint);
return;
} else if (constraint->score >= INFINITY) {
crm_config_err("%s: Cannot perform manditory colocation"
" between non-colocated group and %s", rsc_lh->id, rsc_rh->id);
return;
}
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t *) gIter->data;
child_rsc->cmds->rsc_colocation_lh(child_rsc, rsc_rh, constraint);
}
}
void
group_rsc_colocation_rh(resource_t * rsc_lh, resource_t * rsc_rh, rsc_colocation_t * constraint)
{
GListPtr gIter = rsc_rh->children;
group_variant_data_t *group_data = NULL;
get_group_variant_data(group_data, rsc_rh);
CRM_CHECK(rsc_lh->variant == pe_native, return);
crm_trace("Processing RH of constraint %s", constraint->id);
print_resource(LOG_DEBUG_3, "LHS", rsc_lh, TRUE);
if (is_set(rsc_rh->flags, pe_rsc_provisional)) {
return;
} else if (group_data->colocated && group_data->first_child) {
if (constraint->score >= INFINITY) {
/* Ensure RHS is _fully_ up before can start LHS */
group_data->last_child->cmds->rsc_colocation_rh(rsc_lh, group_data->last_child,
constraint);
} else {
/* A partially active RHS is fine */
group_data->first_child->cmds->rsc_colocation_rh(rsc_lh, group_data->first_child,
constraint);
}
return;
} else if (constraint->score >= INFINITY) {
crm_config_err("%s: Cannot perform manditory colocation with"
" non-colocated group: %s", rsc_lh->id, rsc_rh->id);
return;
}
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t *) gIter->data;
child_rsc->cmds->rsc_colocation_rh(rsc_lh, child_rsc, constraint);
}
}
enum pe_action_flags
group_action_flags(action_t * action, node_t * node)
{
GListPtr gIter = NULL;
enum pe_action_flags flags = (pe_action_optional | pe_action_runnable | pe_action_pseudo);
for (gIter = action->rsc->children; gIter != NULL; gIter = gIter->next) {
resource_t *child = (resource_t *) gIter->data;
enum action_tasks task = get_complex_task(child, action->task, TRUE);
const char *task_s = task2text(task);
action_t *child_action = find_first_action(child->actions, NULL, task_s, node);
if (child_action) {
enum pe_action_flags child_flags = child->cmds->action_flags(child_action, node);
if (is_set(flags, pe_action_optional)
&& is_set(child_flags, pe_action_optional) == FALSE) {
crm_trace("%s is manditory because of %s", action->uuid, child_action->uuid);
clear_bit_inplace(flags, pe_action_optional);
pe_clear_action_bit(action, pe_action_optional);
}
if (safe_str_neq(task_s, action->task)
&& is_set(flags, pe_action_runnable)
&& is_set(child_flags, pe_action_runnable) == FALSE) {
crm_trace("%s is not runnable because of %s", action->uuid, child_action->uuid);
clear_bit_inplace(flags, pe_action_runnable);
pe_clear_action_bit(action, pe_action_runnable);
}
} else if (task != stop_rsc) {
crm_trace("%s is not runnable because of %s (not found in %s)", action->uuid, task_s,
child->id);
clear_bit_inplace(flags, pe_action_runnable);
}
}
return flags;
}
enum pe_graph_flags
group_update_actions(action_t * first, action_t * then, node_t * node, enum pe_action_flags flags,
enum pe_action_flags filter, enum pe_ordering type)
{
GListPtr gIter = then->rsc->children;
enum pe_graph_flags changed = pe_graph_none;
CRM_ASSERT(then->rsc != NULL);
changed |= native_update_actions(first, then, node, flags, filter, type);
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child = (resource_t *) gIter->data;
action_t *child_action = find_first_action(child->actions, NULL, then->task, node);
if (child_action) {
changed |= child->cmds->update_actions(first, child_action, node, flags, filter, type);
}
}
return changed;
}
void
group_rsc_location(resource_t * rsc, rsc_to_node_t * constraint)
{
GListPtr gIter = rsc->children;
GListPtr saved = constraint->node_list_rh;
GListPtr zero = node_list_dup(constraint->node_list_rh, TRUE, FALSE);
gboolean reset_scores = TRUE;
group_variant_data_t *group_data = NULL;
get_group_variant_data(group_data, rsc);
crm_debug("Processing rsc_location %s for %s", constraint->id, rsc->id);
native_rsc_location(rsc, constraint);
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t *) gIter->data;
child_rsc->cmds->rsc_location(child_rsc, constraint);
if (group_data->colocated && reset_scores) {
reset_scores = FALSE;
constraint->node_list_rh = zero;
}
}
constraint->node_list_rh = saved;
- slist_basic_destroy(zero);
+ g_list_free_full(zero, free);
}
void
group_expand(resource_t * rsc, pe_working_set_t * data_set)
{
GListPtr gIter = rsc->children;
group_variant_data_t *group_data = NULL;
get_group_variant_data(group_data, rsc);
crm_trace("Processing actions from %s", rsc->id);
CRM_CHECK(rsc != NULL, return);
native_expand(rsc, data_set);
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t *) gIter->data;
child_rsc->cmds->expand(child_rsc, data_set);
}
}
GHashTable *
group_merge_weights(resource_t * rsc, const char *rhs, GHashTable * nodes, const char *attr,
int factor, gboolean allow_rollback, gboolean only_positive)
{
GListPtr gIter = rsc->rsc_cons_lhs;
group_variant_data_t *group_data = NULL;
get_group_variant_data(group_data, rsc);
if (is_set(rsc->flags, pe_rsc_merging)) {
crm_info("Breaking dependency loop with %s at %s", rsc->id, rhs);
return nodes;
}
set_bit(rsc->flags, pe_rsc_merging);
nodes =
group_data->first_child->cmds->merge_weights(group_data->first_child, rhs, nodes, attr,
factor, allow_rollback, only_positive);
for (; gIter != NULL; gIter = gIter->next) {
rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data;
nodes = native_merge_weights(constraint->rsc_lh, rsc->id, nodes,
constraint->node_attribute,
constraint->score / INFINITY, allow_rollback, only_positive);
}
clear_bit(rsc->flags, pe_rsc_merging);
return nodes;
}
void
group_append_meta(resource_t * rsc, xmlNode * xml)
{
}
diff --git a/pengine/utils.c b/pengine/utils.c
index 636ef57de2..c88b4f485c 100644
--- a/pengine/utils.c
+++ b/pengine/utils.c
@@ -1,511 +1,511 @@
/*
* 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/msg_xml.h>
#include <allocate.h>
#include <utils.h>
#include <lib/pengine/utils.h>
void
pe_free_ordering(GListPtr constraints)
{
GListPtr iterator = constraints;
while (iterator != NULL) {
order_constraint_t *order = iterator->data;
iterator = iterator->next;
free(order->lh_action_task);
free(order->rh_action_task);
free(order);
}
if (constraints != NULL) {
g_list_free(constraints);
}
}
void
pe_free_rsc_to_node(GListPtr constraints)
{
GListPtr iterator = constraints;
while (iterator != NULL) {
rsc_to_node_t *cons = iterator->data;
iterator = iterator->next;
- slist_basic_destroy(cons->node_list_rh);
+ g_list_free_full(cons->node_list_rh, free);
free(cons);
}
if (constraints != NULL) {
g_list_free(constraints);
}
}
rsc_to_node_t *
rsc2node_new(const char *id, resource_t * rsc,
int node_weight, node_t * foo_node, pe_working_set_t * data_set)
{
rsc_to_node_t *new_con = NULL;
if (rsc == NULL || id == NULL) {
pe_err("Invalid constraint %s for rsc=%p", crm_str(id), rsc);
return NULL;
} else if (foo_node == NULL) {
CRM_CHECK(node_weight == 0, return NULL);
}
new_con = calloc(1, sizeof(rsc_to_node_t));
if (new_con != NULL) {
new_con->id = id;
new_con->rsc_lh = rsc;
new_con->node_list_rh = NULL;
new_con->role_filter = RSC_ROLE_UNKNOWN;
if (foo_node != NULL) {
node_t *copy = node_copy(foo_node);
copy->weight = merge_weights(node_weight, foo_node->weight);
new_con->node_list_rh = g_list_prepend(NULL, copy);
}
data_set->placement_constraints = g_list_prepend(data_set->placement_constraints, new_con);
rsc->rsc_location = g_list_prepend(rsc->rsc_location, new_con);
}
return new_con;
}
gboolean
can_run_resources(const node_t * node)
{
if (node == NULL) {
return FALSE;
}
#if 0
if (node->weight < 0) {
return FALSE;
}
#endif
if (node->details->online == FALSE
|| node->details->shutdown || node->details->unclean || node->details->standby) {
crm_trace("%s: online=%d, unclean=%d, standby=%d",
node->details->uname, node->details->online,
node->details->unclean, node->details->standby);
return FALSE;
}
return TRUE;
}
struct compare_data {
const node_t *node1;
const node_t *node2;
int result;
};
static void
do_compare_capacity1(gpointer key, gpointer value, gpointer user_data)
{
int node1_capacity = 0;
int node2_capacity = 0;
struct compare_data *data = user_data;
node1_capacity = crm_parse_int(value, "0");
node2_capacity =
crm_parse_int(g_hash_table_lookup(data->node2->details->utilization, key), "0");
if (node1_capacity > node2_capacity) {
data->result--;
} else if (node1_capacity < node2_capacity) {
data->result++;
}
}
static void
do_compare_capacity2(gpointer key, gpointer value, gpointer user_data)
{
int node1_capacity = 0;
int node2_capacity = 0;
struct compare_data *data = user_data;
if (g_hash_table_lookup_extended(data->node1->details->utilization, key, NULL, NULL)) {
return;
}
node1_capacity = 0;
node2_capacity = crm_parse_int(value, "0");
if (node1_capacity > node2_capacity) {
data->result--;
} else if (node1_capacity < node2_capacity) {
data->result++;
}
}
/* rc < 0 if 'node1' has more capacity remaining
* rc > 0 if 'node1' has less capacity remaining
*/
static int
compare_capacity(const node_t * node1, const node_t * node2)
{
struct compare_data data;
data.node1 = node1;
data.node2 = node2;
data.result = 0;
g_hash_table_foreach(node1->details->utilization, do_compare_capacity1, &data);
g_hash_table_foreach(node2->details->utilization, do_compare_capacity2, &data);
return data.result;
}
/* return -1 if 'a' is more preferred
* return 1 if 'b' is more preferred
*/
gint
sort_node_weight(gconstpointer a, gconstpointer b, gpointer data)
{
const node_t *node1 = (const node_t *)a;
const node_t *node2 = (const node_t *)b;
const node_t *active = (node_t *) data;
int node1_weight = 0;
int node2_weight = 0;
int result = 0;
if (a == NULL) {
return 1;
}
if (b == NULL) {
return -1;
}
node1_weight = node1->weight;
node2_weight = node2->weight;
if (can_run_resources(node1) == FALSE) {
node1_weight = -INFINITY;
}
if (can_run_resources(node2) == FALSE) {
node2_weight = -INFINITY;
}
if (node1_weight > node2_weight) {
crm_trace( "%s (%d) > %s (%d) : weight",
node1->details->uname, node1_weight,
node2->details->uname, node2_weight);
return -1;
}
if (node1_weight < node2_weight) {
crm_trace( "%s (%d) < %s (%d) : weight",
node1->details->uname, node1_weight,
node2->details->uname, node2_weight);
return 1;
}
crm_trace( "%s (%d) == %s (%d) : weight",
node1->details->uname, node1_weight, node2->details->uname, node2_weight);
if (safe_str_eq(pe_dataset->placement_strategy, "minimal")) {
goto equal;
}
if (safe_str_eq(pe_dataset->placement_strategy, "balanced")) {
result = compare_capacity(node1, node2);
if (result != 0) {
return result;
}
}
/* now try to balance resources across the cluster */
if (node1->details->num_resources < node2->details->num_resources) {
crm_trace( "%s (%d) < %s (%d) : resources",
node1->details->uname, node1->details->num_resources,
node2->details->uname, node2->details->num_resources);
return -1;
} else if (node1->details->num_resources > node2->details->num_resources) {
crm_trace( "%s (%d) > %s (%d) : resources",
node1->details->uname, node1->details->num_resources,
node2->details->uname, node2->details->num_resources);
return 1;
}
if (active && active->details == node1->details) {
crm_trace( "%s (%d) > %s (%d) : active",
node1->details->uname, node1->details->num_resources,
node2->details->uname, node2->details->num_resources);
return -1;
} else if (active && active->details == node2->details) {
crm_trace( "%s (%d) > %s (%d) : active",
node1->details->uname, node1->details->num_resources,
node2->details->uname, node2->details->num_resources);
return 1;
}
equal:
crm_trace( "%s = %s", node1->details->uname, node2->details->uname);
return strcmp(node1->details->uname, node2->details->uname);
}
struct calculate_data {
node_t *node;
gboolean allocate;
};
static void
do_calculate_utilization(gpointer key, gpointer value, gpointer user_data)
{
const char *capacity = NULL;
char *remain_capacity = NULL;
struct calculate_data *data = user_data;
capacity = g_hash_table_lookup(data->node->details->utilization, key);
if (capacity) {
if (data->allocate) {
remain_capacity = crm_itoa(crm_parse_int(capacity, "0") - crm_parse_int(value, "0"));
} else {
remain_capacity = crm_itoa(crm_parse_int(capacity, "0") + crm_parse_int(value, "0"));
}
g_hash_table_replace(data->node->details->utilization, crm_strdup(key), remain_capacity);
}
}
/* Specify 'allocate' to TRUE when allocating
* Otherwise to FALSE when deallocating
*/
static void
calculate_utilization(node_t * node, resource_t * rsc, gboolean allocate)
{
struct calculate_data data;
data.node = node;
data.allocate = allocate;
g_hash_table_foreach(rsc->utilization, do_calculate_utilization, &data);
if (allocate) {
dump_rsc_utilization(show_utilization ? 0 : utilization_log_level, __FUNCTION__, rsc, node);
}
}
void
native_deallocate(resource_t * rsc)
{
if (rsc->allocated_to) {
node_t *old = rsc->allocated_to;
crm_info("Deallocating %s from %s", rsc->id, old->details->uname);
set_bit_inplace(rsc->flags, pe_rsc_provisional);
rsc->allocated_to = NULL;
old->details->allocated_rsc = g_list_remove(old->details->allocated_rsc, rsc);
old->details->num_resources--;
/* old->count--; */
calculate_utilization(old, rsc, FALSE);
free(old);
}
}
gboolean
native_assign_node(resource_t * rsc, GListPtr nodes, node_t * chosen, gboolean force)
{
CRM_ASSERT(rsc->variant == pe_native);
if (force == FALSE
&& chosen != NULL && (can_run_resources(chosen) == FALSE || chosen->weight < 0)) {
crm_debug("All nodes for resource %s are unavailable"
", unclean or shutting down (%s: %d, %d)",
rsc->id, chosen->details->uname, can_run_resources(chosen), chosen->weight);
rsc->next_role = RSC_ROLE_STOPPED;
chosen = NULL;
}
/* todo: update the old node for each resource to reflect its
* new resource count
*/
native_deallocate(rsc);
clear_bit(rsc->flags, pe_rsc_provisional);
if (chosen == NULL) {
char *key = NULL;
GListPtr gIter = NULL;
GListPtr possible_matches = NULL;
crm_debug("Could not allocate a node for %s", rsc->id);
rsc->next_role = RSC_ROLE_STOPPED;
key = generate_op_key(rsc->id, RSC_STOP, 0);
possible_matches = find_actions(rsc->actions, key, NULL);
for (gIter = possible_matches; gIter != NULL; gIter = gIter->next) {
action_t *stop = (action_t *) gIter->data;
update_action_flags(stop, pe_action_optional | pe_action_clear);
}
g_list_free(possible_matches);
free(key);
key = generate_op_key(rsc->id, RSC_START, 0);
possible_matches = find_actions(rsc->actions, key, NULL);
for (gIter = possible_matches; gIter != NULL; gIter = gIter->next) {
action_t *start = (action_t *) gIter->data;
update_action_flags(start, pe_action_runnable | pe_action_clear);
}
g_list_free(possible_matches);
free(key);
return FALSE;
}
crm_debug("Assigning %s to %s", chosen->details->uname, rsc->id);
rsc->allocated_to = node_copy(chosen);
chosen->details->allocated_rsc = g_list_prepend(chosen->details->allocated_rsc, rsc);
chosen->details->num_resources++;
chosen->count++;
calculate_utilization(chosen, rsc, TRUE);
return TRUE;
}
void
log_action(unsigned int log_level, const char *pre_text, action_t * action, gboolean details)
{
const char *node_uname = NULL;
const char *node_uuid = NULL;
if (action == NULL) {
crm_trace( "%s%s: <NULL>",
pre_text == NULL ? "" : pre_text, pre_text == NULL ? "" : ": ");
return;
}
if (is_set(action->flags, pe_action_pseudo)) {
node_uname = NULL;
node_uuid = NULL;
} else if (action->node != NULL) {
node_uname = action->node->details->uname;
node_uuid = action->node->details->id;
} else {
node_uname = "<none>";
node_uuid = NULL;
}
switch (text2task(action->task)) {
case stonith_node:
case shutdown_crm:
crm_trace(
"%s%s%sAction %d: %s%s%s%s%s%s",
pre_text == NULL ? "" : pre_text,
pre_text == NULL ? "" : ": ",
is_set(action->flags,
pe_action_pseudo) ? "Pseduo " : is_set(action->flags,
pe_action_optional) ?
"Optional " : is_set(action->flags,
pe_action_runnable) ? is_set(action->flags,
pe_action_processed)
? "" : "(Provisional) " : "!!Non-Startable!! ", action->id,
action->uuid, node_uname ? "\ton " : "",
node_uname ? node_uname : "", node_uuid ? "\t\t(" : "",
node_uuid ? node_uuid : "", node_uuid ? ")" : "");
break;
default:
crm_trace(
"%s%s%sAction %d: %s %s%s%s%s%s%s",
pre_text == NULL ? "" : pre_text,
pre_text == NULL ? "" : ": ",
is_set(action->flags,
pe_action_optional) ? "Optional " : is_set(action->flags,
pe_action_pseudo)
? "Pseduo " : is_set(action->flags,
pe_action_runnable) ? is_set(action->flags,
pe_action_processed)
? "" : "(Provisional) " : "!!Non-Startable!! ", action->id,
action->uuid, safe_val3("<none>", action, rsc, id),
node_uname ? "\ton " : "", node_uname ? node_uname : "",
node_uuid ? "\t\t(" : "", node_uuid ? node_uuid : "",
node_uuid ? ")" : "");
break;
}
if (details) {
GListPtr gIter = NULL;
crm_trace( "\t\t====== Preceding Actions");
gIter = action->actions_before;
for (; gIter != NULL; gIter = gIter->next) {
action_wrapper_t *other = (action_wrapper_t *) gIter->data;
log_action(log_level + 1, "\t\t", other->action, FALSE);
}
crm_trace( "\t\t====== Subsequent Actions");
gIter = action->actions_after;
for (; gIter != NULL; gIter = gIter->next) {
action_wrapper_t *other = (action_wrapper_t *) gIter->data;
log_action(log_level + 1, "\t\t", other->action, FALSE);
}
crm_trace( "\t\t====== End");
} else {
crm_trace( "\t\t(seen=%d, before=%d, after=%d)",
action->seen_count,
g_list_length(action->actions_before),
g_list_length(action->actions_after));
}
}
gboolean
can_run_any(GHashTable * nodes)
{
GHashTableIter iter;
node_t *node = NULL;
if (nodes == NULL) {
return FALSE;
}
g_hash_table_iter_init(&iter, nodes);
while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
if (can_run_resources(node) && node->weight >= 0) {
return TRUE;
}
}
return FALSE;
}

File Metadata

Mime Type
text/x-diff
Expires
Sat, Nov 23, 7:29 AM (22 h, 15 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1018468
Default Alt Text
(417 KB)

Event Timeline