Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F4624190
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
164 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/include/crm/pengine/status.h b/include/crm/pengine/status.h
index 67327db21e..628a6f4a02 100644
--- a/include/crm/pengine/status.h
+++ b/include/crm/pengine/status.h
@@ -1,379 +1,380 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef PENGINE_STATUS__H
# define PENGINE_STATUS__H
# include <glib.h>
# include <crm/common/iso8601.h>
# include <crm/pengine/common.h>
typedef struct node_s pe_node_t;
typedef struct node_s node_t;
typedef struct pe_action_s action_t;
typedef struct pe_action_s pe_action_t;
typedef struct resource_s resource_t;
typedef struct ticket_s ticket_t;
typedef enum no_quorum_policy_e {
no_quorum_freeze,
no_quorum_stop,
no_quorum_ignore,
no_quorum_suicide
} no_quorum_policy_t;
enum node_type {
node_ping,
node_member,
node_remote
};
enum pe_restart {
pe_restart_restart,
pe_restart_ignore
};
enum pe_find {
pe_find_renamed = 0x001,
pe_find_clone = 0x004,
pe_find_current = 0x008,
pe_find_inactive = 0x010,
};
# define pe_flag_have_quorum 0x00000001ULL
# define pe_flag_symmetric_cluster 0x00000002ULL
# define pe_flag_is_managed_default 0x00000004ULL
# define pe_flag_maintenance_mode 0x00000008ULL
# define pe_flag_stonith_enabled 0x00000010ULL
# define pe_flag_have_stonith_resource 0x00000020ULL
# define pe_flag_stop_rsc_orphans 0x00000100ULL
# define pe_flag_stop_action_orphans 0x00000200ULL
# define pe_flag_stop_everything 0x00000400ULL
# define pe_flag_start_failure_fatal 0x00001000ULL
# define pe_flag_remove_after_stop 0x00002000ULL
# define pe_flag_startup_probes 0x00010000ULL
# define pe_flag_have_status 0x00020000ULL
# define pe_flag_have_remote_nodes 0x00040000ULL
# define pe_flag_quick_location 0x00100000ULL
typedef struct pe_working_set_s {
xmlNode *input;
crm_time_t *now;
/* options extracted from the input */
char *dc_uuid;
node_t *dc_node;
const char *stonith_action;
const char *placement_strategy;
unsigned long long flags;
int stonith_timeout;
int default_resource_stickiness;
no_quorum_policy_t no_quorum_policy;
GHashTable *config_hash;
GHashTable *domains;
GHashTable *tickets;
+ GHashTable *singletons; /* Actions for which there can be only one - ie. fence nodeX */
GListPtr nodes;
GListPtr resources;
GListPtr placement_constraints;
GListPtr ordering_constraints;
GListPtr colocation_constraints;
GListPtr ticket_constraints;
GListPtr actions;
xmlNode *failed;
xmlNode *op_defaults;
xmlNode *rsc_defaults;
/* stats */
int num_synapse;
int max_valid_nodes;
int order_id;
int action_id;
/* final output */
xmlNode *graph;
GHashTable *template_rsc_sets;
const char *localhost;
} pe_working_set_t;
struct node_shared_s {
const char *id;
const char *uname;
/* Make all these flags into a bitfield one day */
gboolean online;
gboolean standby;
gboolean standby_onfail;
gboolean pending;
gboolean unclean;
gboolean unseen;
gboolean shutdown;
gboolean expected_up;
gboolean is_dc;
int num_resources;
GListPtr running_rsc; /* resource_t* */
GListPtr allocated_rsc; /* resource_t* */
resource_t *remote_rsc;
GHashTable *attrs; /* char* => char* */
enum node_type type;
GHashTable *utilization;
/*! cache of calculated rsc digests for this node. */
GHashTable *digest_cache;
gboolean maintenance;
};
struct node_s {
int weight;
gboolean fixed;
int count;
struct node_shared_s *details;
};
# include <crm/pengine/complex.h>
# define pe_rsc_orphan 0x00000001ULL
# define pe_rsc_managed 0x00000002ULL
# define pe_rsc_block 0x00000004ULL /* Further operations are prohibited due to failure policy */
# define pe_rsc_orphan_container_filler 0x00000008ULL
# define pe_rsc_notify 0x00000010ULL
# define pe_rsc_unique 0x00000020ULL
# define pe_rsc_provisional 0x00000100ULL
# define pe_rsc_allocating 0x00000200ULL
# define pe_rsc_merging 0x00000400ULL
# define pe_rsc_munging 0x00000800ULL
# define pe_rsc_try_reload 0x00001000ULL
# define pe_rsc_reload 0x00002000ULL
# define pe_rsc_failed 0x00010000ULL
# define pe_rsc_shutdown 0x00020000ULL
# define pe_rsc_runnable 0x00040000ULL
# define pe_rsc_start_pending 0x00080000ULL
# define pe_rsc_starting 0x00100000ULL
# define pe_rsc_stopping 0x00200000ULL
# define pe_rsc_migrating 0x00400000ULL
# define pe_rsc_allow_migrate 0x00800000ULL
# define pe_rsc_failure_ignored 0x01000000ULL
# define pe_rsc_unexpectedly_running 0x02000000ULL
# define pe_rsc_maintenance 0x04000000ULL
# define pe_rsc_needs_quorum 0x10000000ULL
# define pe_rsc_needs_fencing 0x20000000ULL
# define pe_rsc_needs_unfencing 0x40000000ULL
enum pe_graph_flags {
pe_graph_none = 0x00000,
pe_graph_updated_first = 0x00001,
pe_graph_updated_then = 0x00002,
pe_graph_disable = 0x00004,
};
/* *INDENT-OFF* */
enum pe_action_flags {
pe_action_pseudo = 0x00001,
pe_action_runnable = 0x00002,
pe_action_optional = 0x00004,
pe_action_print_always = 0x00008,
pe_action_have_node_attrs = 0x00010,
pe_action_failure_is_fatal = 0x00020,
pe_action_implied_by_stonith = 0x00040,
pe_action_migrate_runnable = 0x00080,
pe_action_dumped = 0x00100,
pe_action_processed = 0x00200,
pe_action_clear = 0x00400,
pe_action_dangle = 0x00800,
pe_action_requires_any = 0x01000, /* This action requires one or mre of its dependancies to be runnable
* We use this to clear the runnable flag before checking dependancies
*/
};
/* *INDENT-ON* */
struct resource_s {
char *id;
char *clone_name;
xmlNode *xml;
xmlNode *orig_xml;
xmlNode *ops_xml;
resource_t *parent;
void *variant_opaque;
enum pe_obj_types variant;
resource_object_functions_t *fns;
resource_alloc_functions_t *cmds;
enum rsc_recovery_type recovery_type;
enum pe_restart restart_type;
int priority;
int stickiness;
int sort_index;
int failure_timeout;
int effective_priority;
int migration_threshold;
gboolean is_remote_node;
unsigned long long flags;
GListPtr rsc_cons_lhs; /* rsc_colocation_t* */
GListPtr rsc_cons; /* rsc_colocation_t* */
GListPtr rsc_location; /* rsc_to_node_t* */
GListPtr actions; /* action_t* */
GListPtr rsc_tickets; /* rsc_ticket* */
node_t *allocated_to;
GListPtr running_on; /* node_t* */
GHashTable *known_on; /* node_t* */
GHashTable *allowed_nodes; /* node_t* */
enum rsc_role_e role;
enum rsc_role_e next_role;
GHashTable *meta;
GHashTable *parameters;
GHashTable *utilization;
GListPtr children; /* resource_t* */
GListPtr dangling_migrations; /* node_t* */
node_t *partial_migration_target;
node_t *partial_migration_source;
resource_t *container;
GListPtr fillers;
char *pending_task;
};
struct pe_action_s {
int id;
int priority;
resource_t *rsc;
node_t *node;
xmlNode *op_entry;
char *task;
char *uuid;
char *cancel_task;
enum pe_action_flags flags;
enum rsc_start_requirement needs;
enum action_fail_response on_fail;
enum rsc_role_e fail_role;
action_t *pre_notify;
action_t *pre_notified;
action_t *post_notify;
action_t *post_notified;
int seen_count;
GHashTable *meta;
GHashTable *extra;
GListPtr actions_before; /* action_warpper_t* */
GListPtr actions_after; /* action_warpper_t* */
};
struct ticket_s {
char *id;
gboolean granted;
time_t last_granted;
gboolean standby;
GHashTable *state;
};
enum pe_link_state {
pe_link_not_dumped,
pe_link_dumped,
pe_link_dup,
};
/* *INDENT-OFF* */
enum pe_ordering {
pe_order_none = 0x0, /* deleted */
pe_order_optional = 0x1, /* pure ordering, nothing implied */
pe_order_apply_first_non_migratable = 0x2, /* Only apply this constraint's ordering if first is not migratable. */
pe_order_implies_first = 0x10, /* If 'then' is required, ensure 'first' is too */
pe_order_implies_then = 0x20, /* If 'first' is required, ensure 'then' is too */
pe_order_implies_first_master = 0x40, /* Imply 'first' is required when 'then' is required and then's rsc holds Master role. */
/* first requires then to be both runnable and migrate runnable. */
pe_order_implies_first_migratable = 0x80,
pe_order_runnable_left = 0x100, /* 'then' requires 'first' to be runnable */
pe_order_pseudo_left = 0x200, /* 'then' can only be pseudo if 'first' is runnable */
pe_order_restart = 0x1000, /* 'then' is runnable if 'first' is optional or runnable */
pe_order_stonith_stop = 0x2000, /* only applies if the action is non-pseudo */
pe_order_serialize_only = 0x4000, /* serialize */
pe_order_implies_first_printed = 0x10000, /* Like ..implies_first but only ensures 'first' is printed, not manditory */
pe_order_implies_then_printed = 0x20000, /* Like ..implies_then but only ensures 'then' is printed, not manditory */
pe_order_asymmetrical = 0x100000, /* Indicates asymmetrical one way ordering constraint. */
pe_order_load = 0x200000, /* Only relevant if... */
pe_order_one_or_more = 0x400000, /* 'then' is only runnable if one or more of it's dependancies are too */
pe_order_anti_colocation = 0x800000,
pe_order_trace = 0x4000000, /* test marker */
};
/* *INDENT-ON* */
typedef struct action_wrapper_s action_wrapper_t;
struct action_wrapper_s {
enum pe_ordering type;
enum pe_link_state state;
action_t *action;
};
const char *rsc_printable_id(resource_t *rsc);
gboolean cluster_status(pe_working_set_t * data_set);
void set_working_set_defaults(pe_working_set_t * data_set);
void cleanup_calculations(pe_working_set_t * data_set);
resource_t *pe_find_resource(GListPtr rsc_list, const char *id_rh);
node_t *pe_find_node(GListPtr node_list, const char *uname);
node_t *pe_find_node_id(GListPtr node_list, const char *id);
node_t *pe_find_node_any(GListPtr node_list, const char *id, const char *uname);
GListPtr find_operations(const char *rsc, const char *node, gboolean active_filter,
pe_working_set_t * data_set);
#endif
diff --git a/lib/pengine/status.c b/lib/pengine/status.c
index 4c30db14ea..ef60f05a37 100644
--- a/lib/pengine/status.c
+++ b/lib/pengine/status.c
@@ -1,307 +1,311 @@
/*
* 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 <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <glib.h>
#include <crm/pengine/internal.h>
#include <unpack.h>
#define MEMCHECK_STAGE_0 0
#define check_and_exit(stage) cleanup_calculations(data_set); \
crm_mem_stats(NULL); \
crm_err("Exiting: stage %d", stage); \
crm_exit(pcmk_err_generic);
/*
* Unpack everything
* At the end you'll have:
* - A list of nodes
* - A list of resources (each with any dependencies on other resources)
* - A list of constraints between resources and nodes
* - A list of constraints between start/stop actions
* - A list of nodes that need to be stonith'd
* - A list of nodes that need to be shutdown
* - A list of the possible stop/start actions (without dependencies)
*/
gboolean
cluster_status(pe_working_set_t * data_set)
{
xmlNode *config = get_xpath_object("//"XML_CIB_TAG_CRMCONFIG, data_set->input, LOG_TRACE);
xmlNode *cib_nodes = get_xpath_object("//"XML_CIB_TAG_NODES, data_set->input, LOG_TRACE);
xmlNode *cib_resources = get_xpath_object("//"XML_CIB_TAG_RESOURCES, data_set->input, LOG_TRACE);
xmlNode *cib_status = get_xpath_object("//"XML_CIB_TAG_STATUS, data_set->input, LOG_TRACE);
xmlNode *cib_domains = get_xpath_object("//"XML_CIB_TAG_DOMAINS, data_set->input, LOG_TRACE);
const char *value = crm_element_value(data_set->input, XML_ATTR_HAVE_QUORUM);
crm_trace("Beginning unpack");
pe_dataset = data_set;
/* reset remaining global variables */
data_set->failed = create_xml_node(NULL, "failed-ops");
if (data_set->input == NULL) {
return FALSE;
}
if (data_set->now == NULL) {
data_set->now = crm_time_new(NULL);
}
if (data_set->dc_uuid == NULL
&& data_set->input != NULL
&& crm_element_value(data_set->input, XML_ATTR_DC_UUID) != NULL) {
/* this should always be present */
data_set->dc_uuid = crm_element_value_copy(data_set->input, XML_ATTR_DC_UUID);
}
clear_bit(data_set->flags, pe_flag_have_quorum);
if (crm_is_true(value)) {
set_bit(data_set->flags, pe_flag_have_quorum);
}
data_set->op_defaults = get_xpath_object("//"XML_CIB_TAG_OPCONFIG, data_set->input, LOG_TRACE);
data_set->rsc_defaults = get_xpath_object("//"XML_CIB_TAG_RSCCONFIG, data_set->input, LOG_TRACE);
unpack_config(config, data_set);
if (is_not_set(data_set->flags, pe_flag_quick_location)
&& is_not_set(data_set->flags, pe_flag_have_quorum)
&& data_set->no_quorum_policy != no_quorum_ignore) {
crm_warn("We do not have quorum - fencing and resource management disabled");
}
unpack_nodes(cib_nodes, data_set);
unpack_domains(cib_domains, data_set);
if(is_not_set(data_set->flags, pe_flag_quick_location)) {
unpack_remote_nodes(cib_resources, data_set);
}
unpack_resources(cib_resources, data_set);
if(is_not_set(data_set->flags, pe_flag_quick_location)) {
unpack_status(cib_status, data_set);
}
set_bit(data_set->flags, pe_flag_have_status);
return TRUE;
}
static void
pe_free_resources(GListPtr resources)
{
resource_t *rsc = NULL;
GListPtr iterator = resources;
while (iterator != NULL) {
rsc = (resource_t *) iterator->data;
iterator = iterator->next;
rsc->fns->free(rsc);
}
if (resources != NULL) {
g_list_free(resources);
}
}
static void
pe_free_actions(GListPtr actions)
{
GListPtr iterator = actions;
while (iterator != NULL) {
pe_free_action(iterator->data);
iterator = iterator->next;
}
if (actions != NULL) {
g_list_free(actions);
}
}
static void
pe_free_nodes(GListPtr nodes)
{
GListPtr iterator = nodes;
while (iterator != NULL) {
node_t *node = (node_t *) iterator->data;
struct node_shared_s *details = node->details;
iterator = iterator->next;
crm_trace("deleting node");
print_node("delete", node, FALSE);
if (details != NULL) {
crm_trace("%s is being deleted", details->uname);
if (details->attrs != NULL) {
g_hash_table_destroy(details->attrs);
}
if (details->utilization != NULL) {
g_hash_table_destroy(details->utilization);
}
if (details->digest_cache != NULL) {
g_hash_table_destroy(details->digest_cache);
}
g_list_free(details->running_rsc);
g_list_free(details->allocated_rsc);
free(details);
}
free(node);
}
if (nodes != NULL) {
g_list_free(nodes);
}
}
void
cleanup_calculations(pe_working_set_t * data_set)
{
pe_dataset = NULL;
if (data_set == NULL) {
return;
}
clear_bit(data_set->flags, pe_flag_have_status);
if (data_set->config_hash != NULL) {
g_hash_table_destroy(data_set->config_hash);
}
+ if (data_set->singletons != NULL) {
+ g_hash_table_destroy(data_set->singletons);
+ }
+
if (data_set->tickets) {
g_hash_table_destroy(data_set->tickets);
}
if (data_set->template_rsc_sets) {
g_hash_table_destroy(data_set->template_rsc_sets);
}
free(data_set->dc_uuid);
crm_trace("deleting resources");
pe_free_resources(data_set->resources);
crm_trace("deleting actions");
pe_free_actions(data_set->actions);
if (data_set->domains) {
g_hash_table_destroy(data_set->domains);
}
crm_trace("deleting nodes");
pe_free_nodes(data_set->nodes);
free_xml(data_set->graph);
crm_time_free(data_set->now);
free_xml(data_set->input);
free_xml(data_set->failed);
set_working_set_defaults(data_set);
CRM_CHECK(data_set->ordering_constraints == NULL,;
);
CRM_CHECK(data_set->placement_constraints == NULL,;
);
}
void
set_working_set_defaults(pe_working_set_t * data_set)
{
pe_dataset = data_set;
memset(data_set, 0, sizeof(pe_working_set_t));
data_set->dc_uuid = NULL;
data_set->order_id = 1;
data_set->action_id = 1;
data_set->no_quorum_policy = no_quorum_freeze;
data_set->flags = 0x0ULL;
set_bit(data_set->flags, pe_flag_stop_rsc_orphans);
set_bit(data_set->flags, pe_flag_symmetric_cluster);
set_bit(data_set->flags, pe_flag_is_managed_default);
set_bit(data_set->flags, pe_flag_stop_action_orphans);
}
resource_t *
pe_find_resource(GListPtr rsc_list, const char *id)
{
GListPtr rIter = NULL;
for (rIter = rsc_list; id && rIter; rIter = rIter->next) {
resource_t *parent = rIter->data;
resource_t *match =
parent->fns->find_rsc(parent, id, NULL, pe_find_renamed | pe_find_current);
if (match != NULL) {
return match;
}
}
crm_trace("No match for %s", id);
return NULL;
}
node_t *
pe_find_node_any(GListPtr nodes, const char *id, const char *uname)
{
node_t *match = pe_find_node_id(nodes, id);
if (match) {
return match;
}
crm_trace("Looking up %s via it's uname instead", uname);
return pe_find_node(nodes, uname);
}
node_t *
pe_find_node_id(GListPtr nodes, const char *id)
{
GListPtr gIter = nodes;
for (; gIter != NULL; gIter = gIter->next) {
node_t *node = (node_t *) gIter->data;
if (node && safe_str_eq(node->details->id, id)) {
return node;
}
}
/* error */
return NULL;
}
node_t *
pe_find_node(GListPtr nodes, const char *uname)
{
GListPtr gIter = nodes;
for (; gIter != NULL; gIter = gIter->next) {
node_t *node = (node_t *) gIter->data;
if (node && safe_str_eq(node->details->uname, uname)) {
return node;
}
}
/* error */
return NULL;
}
diff --git a/lib/pengine/utils.c b/lib/pengine/utils.c
index 8443571699..98c2dc30a3 100644
--- a/lib/pengine/utils.c
+++ b/lib/pengine/utils.c
@@ -1,1845 +1,1854 @@
/*
* 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 <crm/pengine/internal.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, resource_t * container,
pe_working_set_t * data_set);
static xmlNode *find_rsc_op_entry_helper(resource_t * rsc, const char *key,
gboolean include_disabled);
bool pe_can_fence(pe_working_set_t * data_set, node_t *node)
{
if(is_not_set(data_set->flags, pe_flag_stonith_enabled)) {
return FALSE; /* Turned off */
} else if (is_not_set(data_set->flags, pe_flag_have_stonith_resource)) {
return FALSE; /* No devices */
} else if (is_set(data_set->flags, pe_flag_have_quorum)) {
return TRUE;
} else if (data_set->no_quorum_policy == no_quorum_ignore) {
return TRUE;
} else if(node == NULL) {
return FALSE;
} else if(node->details->online) {
crm_notice("We can fence %s without quorum because they're in our membership", node->details->uname);
return TRUE;
}
crm_trace("Cannot fence %s", node->details->uname);
return FALSE;
}
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;
}
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) {
char score[128];
int len = sizeof(score);
/* 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;
/* This function is called a whole lot, use stack allocated score */
score2char_stack(node->weight, score, len);
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);
}
}
g_list_free(list);
} else if (hash) {
char score[128];
int len = sizeof(score);
g_hash_table_iter_init(&iter, hash);
while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
/* This function is called a whole lot, use stack allocated score */
score2char_stack(node->weight, score, len);
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);
}
}
}
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, free(key); return NULL);
if (save_action && rsc != NULL) {
possible_matches = find_actions(rsc->actions, key, on_node);
+ } else if(save_action) {
+#if 0
+ action = g_hash_table_lookup(data_set->singletons, key);
+#else
+ /* More expensive but takes 'node' into account */
+ possible_matches = find_actions(data_set->actions, key, on_node);
+#endif
+ }
+
+ if(data_set->singletons == NULL) {
+ data_set->singletons = g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, NULL);
}
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);
pe_rsc_trace(rsc, "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) {
pe_rsc_trace(rsc, "Creating%s action %d: %s for %s on %s %d",
optional ? "" : " manditory", data_set->action_id, key,
rsc ? rsc->id : "<NULL>", on_node ? on_node->details->uname : "<NULL>", optional);
}
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 = strdup(task);
if (on_node) {
action->node = node_copy(on_node);
}
action->uuid = strdup(key);
pe_set_action_bit(action, pe_action_failure_is_fatal);
pe_set_action_bit(action, pe_action_runnable);
if (optional) {
pe_rsc_trace(rsc, "Set optional on %s", action->uuid);
pe_set_action_bit(action, pe_action_optional);
} else {
pe_clear_action_bit(action, pe_action_optional);
pe_rsc_trace(rsc, "Unset optional on %s", action->uuid);
}
/*
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) {
+ g_hash_table_insert(data_set->singletons, action->uuid, action);
+ }
}
if (rsc != NULL) {
action->op_entry = find_rsc_op_entry_helper(rsc, key, TRUE);
unpack_operation(action, action->op_entry, rsc->container, data_set);
if (save_action) {
rsc->actions = g_list_prepend(rsc->actions, action);
}
}
if (save_action) {
pe_rsc_trace(rsc, "Action %d created", action->id);
}
}
if (optional == FALSE) {
pe_rsc_trace(rsc, "Unset optional on %s", 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_TRACE;
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_rsc_trace(rsc, "Unset runnable on %s", action->uuid);
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_rsc_trace(rsc, "Set optional on %s", 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)
&& action->node->details->unclean == FALSE && 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) {
pe_rsc_trace(rsc, "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) {
pe_rsc_trace(rsc, "Check resource is already active");
if (rsc->fns->active(rsc, TRUE) == FALSE) {
pe_clear_action_bit(action, pe_action_runnable);
pe_rsc_debug(rsc, "%s\t%s (cancelled : quorum freeze)",
action->node->details->uname, action->uuid);
}
} else {
pe_rsc_trace(rsc, "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;
}
static const char *
unpack_operation_on_fail(action_t * action)
{
const char *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);
return NULL;
} else if (safe_str_eq(action->task, CRMD_ACTION_DEMOTE) && !value) {
/* demote on_fail defaults to master monitor value if present */
xmlNode *operation = NULL;
const char *name = NULL;
const char *role = NULL;
const char *on_fail = NULL;
const char *interval = NULL;
const char *enabled = NULL;
CRM_CHECK(action->rsc != NULL, return NULL);
for (operation = __xml_first_child(action->rsc->ops_xml);
operation && !value; operation = __xml_next(operation)) {
if (!crm_str_eq((const char *)operation->name, "op", TRUE)) {
continue;
}
name = crm_element_value(operation, "name");
role = crm_element_value(operation, "role");
on_fail = crm_element_value(operation, XML_OP_ATTR_ON_FAIL);
enabled = crm_element_value(operation, "enabled");
interval = crm_element_value(operation, XML_LRM_ATTR_INTERVAL);
if (!on_fail) {
continue;
} else if (enabled && !crm_is_true(enabled)) {
continue;
} else if (safe_str_neq(name, "monitor") || safe_str_neq(role, "Master")) {
continue;
} else if (crm_get_interval(interval) <= 0) {
continue;
}
value = on_fail;
}
}
return value;
}
static xmlNode *
find_min_interval_mon(resource_t * rsc, gboolean include_disabled)
{
int number = 0;
int min_interval = -1;
const char *name = NULL;
const char *value = NULL;
const char *interval = NULL;
xmlNode *op = NULL;
xmlNode *operation = NULL;
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;
}
if (safe_str_neq(name, RSC_STATUS)) {
continue;
}
number = crm_get_interval(interval);
if (number < 0) {
continue;
}
if (min_interval < 0 || number < min_interval) {
min_interval = number;
op = operation;
}
}
}
return op;
}
void
unpack_operation(action_t * action, xmlNode * xml_obj, resource_t * container,
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 *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, strdup(prop_name), 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");
/* Begin compatability code */
value = g_hash_table_lookup(action->meta, "requires");
if (safe_str_neq(action->task, RSC_START)
&& safe_str_neq(action->task, RSC_PROMOTE)) {
action->needs = rsc_req_nothing;
value = "nothing (not start/promote)";
} 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 (safe_str_eq(value, "unfencing")) {
action->needs = rsc_req_stonith;
set_bit(action->rsc->flags, pe_rsc_needs_unfencing);
if (is_set(data_set->flags, pe_flag_stonith_enabled)) {
crm_notice("%s requires (un)fencing but fencing is disabled", action->rsc->id);
}
} else if (is_set(data_set->flags, pe_flag_stonith_enabled)
&& safe_str_eq(value, "fencing")) {
action->needs = rsc_req_stonith;
if (is_set(data_set->flags, pe_flag_stonith_enabled)) {
crm_notice("%s requires fencing but fencing is disabled", action->rsc->id);
}
/* End compatability code */
} else if (is_set(action->rsc->flags, pe_rsc_needs_fencing)) {
action->needs = rsc_req_stonith;
value = "fencing (resource)";
} else if (is_set(action->rsc->flags, pe_rsc_needs_quorum)) {
action->needs = rsc_req_quorum;
value = "quorum (resource)";
} else {
action->needs = rsc_req_nothing;
value = "nothing (resource)";
}
pe_rsc_trace(action->rsc, "\tAction %s requires: %s", action->task, value);
value = unpack_operation_on_fail(action);
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 if (safe_str_eq(value, "restart-container")) {
if (container) {
action->on_fail = action_fail_restart_container;
value = "restart container (and possibly migrate)";
} else {
value = NULL;
}
} else {
pe_err("Resource %s: Unknown failure type (%s)", action->rsc->id, value);
value = NULL;
}
/* defaults */
if (value == NULL && container) {
action->on_fail = action_fail_restart_container;
value = "restart container (and possibly migrate) (default)";
} else 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)";
}
pe_rsc_trace(action->rsc, "\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;
}
}
pe_rsc_trace(action->rsc, "\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, 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, strdup(field), value_ms);
} else if (interval > 0 && g_hash_table_lookup(action->meta, XML_OP_ATTR_ORIGIN)) {
crm_time_t *origin = NULL;
value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ORIGIN);
origin = crm_time_new(value);
if (origin == NULL) {
crm_config_err("Operation %s contained an invalid " XML_OP_ATTR_ORIGIN ": %s",
ID(xml_obj), value);
} else {
crm_time_t *delay = NULL;
int rc = crm_time_compare(origin, data_set->now);
unsigned long long delay_s = 0;
while (rc < 0) {
crm_time_add_seconds(origin, interval / 1000);
rc = crm_time_compare(origin, data_set->now);
}
delay = crm_time_subtract(origin, data_set->now);
delay_s = crm_time_get_seconds(delay);
start_delay = delay_s * 1000;
crm_info("Calculated a start delay of %llus for %s", delay_s, ID(xml_obj));
g_hash_table_replace(action->meta, strdup(XML_OP_ATTR_START_DELAY),
crm_itoa(start_delay));
crm_time_free(origin);
crm_time_free(delay);
}
}
field = XML_ATTR_TIMEOUT;
value = g_hash_table_lookup(action->meta, field);
if (value == NULL && xml_obj == NULL && safe_str_eq(action->task, RSC_STATUS) && interval == 0) {
xmlNode *min_interval_mon = find_min_interval_mon(action->rsc, FALSE);
if (min_interval_mon) {
value = crm_element_value(min_interval_mon, XML_ATTR_TIMEOUT);
pe_rsc_trace(action->rsc,
"\t%s uses the timeout value '%s' from the minimum interval monitor",
action->uuid, value);
}
}
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, strdup(field), value_ms);
}
static xmlNode *
find_rsc_op_entry_helper(resource_t * rsc, const char *key, gboolean include_disabled)
{
unsigned long long 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);
match_key = generate_op_key(rsc->id, name, number);
if (safe_str_eq(key, match_key)) {
op = operation;
}
free(match_key);
if (rsc->clone_name) {
match_key = generate_op_key(rsc->clone_name, 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 = 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;
}
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->cancel_task);
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);
}
}
pe_rsc_trace(rsc, "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, XML_RSC_OP_LAST_CHANGE, &last_a);
crm_element_value_const_int(xml_b, XML_RSC_OP_LAST_CHANGE, &last_b);
crm_trace("rc-change: %d vs %d", last_a, 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"));
if(!decode_transition_magic(a_magic, &a_uuid, &a_id, &dummy, &dummy, &dummy, &dummy)) {
sort_return(0, "bad magic a");
}
if(!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_effective_time(pe_working_set_t * data_set)
{
if(data_set) {
if (data_set->now == NULL) {
crm_trace("Recording a new 'now'");
data_set->now = crm_time_new(NULL);
}
return crm_time_get_seconds_since_epoch(data_set->now);
}
crm_trace("Defaulting to 'now'");
return time(NULL);
}
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, time_t *last_failure, pe_working_set_t * data_set)
{
return get_failcount_full(node, rsc, last_failure, TRUE, data_set);
}
int
get_failcount_full(node_t * node, resource_t * rsc, time_t *last_failure, bool effective, pe_working_set_t * data_set)
{
char *key = NULL;
const char *value = NULL;
struct fail_search search = { rsc, 0, 0, NULL };
/* Optimize the "normal" case */
key = crm_concat("fail-count", rsc->clone_name ? rsc->clone_name : rsc->id, '-');
value = g_hash_table_lookup(node->details->attrs, key);
search.count = char2score(value);
crm_trace("%s = %s", key, value);
free(key);
if (value) {
key = crm_concat("last-failure", rsc->clone_name ? rsc->clone_name : rsc->id, '-');
value = g_hash_table_lookup(node->details->attrs, key);
search.last = crm_int_helper(value, NULL);
free(key);
/* This block is still relevant once we omit anonymous instance numbers
* because stopped clones wont have clone_name set
*/
} else if (is_not_set(rsc->flags, pe_rsc_unique)) {
search.rsc = uber_parent(rsc);
search.key = clone_strip(rsc->id);
g_hash_table_foreach(node->details->attrs, get_failcount_by_prefix, &search);
free(search.key);
}
if (search.count != 0 && search.last != 0 && last_failure) {
*last_failure = search.last;
}
if(search.count && rsc->failure_timeout) {
/* Never time-out if blocking failures are configured */
char *xml_name = clone_strip(rsc->id);
char *xpath = g_strdup_printf("//primitive[@id='%s']//op[@on-fail='block']", xml_name);
xmlXPathObject *xpathObj = xpath_search(rsc->xml, xpath);
free(xml_name);
free(xpath);
if (numXpathResults(xpathObj) > 0) {
xmlNode *pref = getXpathResult(xpathObj, 0);
pe_warn("Setting %s.failure_timeout=%d in %s conflicts with on-fail=block: ignoring timeout", rsc->id, rsc->failure_timeout, ID(pref));
rsc->failure_timeout = 0;
#if 0
/* A good idea? */
} else if (rsc->container == NULL && is_not_set(data_set->flags, pe_flag_stonith_enabled)) {
/* In this case, stop.on-fail defaults to block in unpack_operation() */
rsc->failure_timeout = 0;
#endif
}
freeXpathObject(xpathObj);
}
if (effective && search.count != 0 && search.last != 0 && rsc->failure_timeout) {
if (search.last > 0) {
time_t now = get_effective_time(data_set);
if (now > (search.last + rsc->failure_timeout)) {
crm_debug("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);
}
return search.count;
}
/* If it's a resource container, get its failcount plus all the failcounts of the resources within it */
int
get_failcount_all(node_t * node, resource_t * rsc, time_t *last_failure, pe_working_set_t * data_set)
{
int failcount_all = 0;
failcount_all = get_failcount(node, rsc, last_failure, data_set);
if (rsc->fillers) {
GListPtr gIter = NULL;
for (gIter = rsc->fillers; gIter != NULL; gIter = gIter->next) {
resource_t *filler = (resource_t *) gIter->data;
time_t filler_last_failure = 0;
failcount_all += get_failcount(node, filler, &filler_last_failure, data_set);
if (last_failure && filler_last_failure > *last_failure) {
*last_failure = filler_last_failure;
}
}
if (failcount_all != 0) {
char *score = score2char(failcount_all);
crm_info("Container %s and the resources within it have failed %s times on %s",
rsc->id, score, node->details->uname);
free(score);
}
}
return failcount_all;
}
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);
+ /* Ensure we never create a dependancy on ourselves... its happened */
+ CRM_ASSERT(lh_action != rh_action);
+
/* 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, strdup(op_s), op_s, NULL, TRUE, TRUE, data_set);
+ if(data_set->singletons) {
+ op = g_hash_table_lookup(data_set->singletons, name);
+ }
+ if (op == NULL) {
+ op = custom_action(NULL, strdup(name), name, NULL, TRUE, TRUE, data_set);
set_bit(op->flags, pe_action_pseudo);
set_bit(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 = 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, strdup(ticket->id), ticket);
}
return ticket;
}
op_digest_cache_t *
rsc_action_digest_cmp(resource_t * rsc, xmlNode * xml_op, node_t * node,
pe_working_set_t * data_set)
{
op_digest_cache_t *data = NULL;
GHashTable *local_rsc_params = NULL;
action_t *action = NULL;
char *key = NULL;
int interval = 0;
const char *op_id = ID(xml_op);
const char *interval_s = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL);
const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK);
const char *digest_all;
const char *digest_restart;
const char *restart_list;
const char *op_version;
data = g_hash_table_lookup(node->details->digest_cache, op_id);
if (data) {
return data;
}
data = calloc(1, sizeof(op_digest_cache_t));
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);
op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION);
/* key is freed in custom_action */
interval = crm_parse_int(interval_s, "0");
key = generate_op_key(rsc->id, task, interval);
action = custom_action(rsc, key, task, node, TRUE, FALSE, data_set);
key = NULL;
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, node, data_set);
data->params_all = create_xml_node(NULL, XML_TAG_PARAMS);
g_hash_table_foreach(local_rsc_params, hash2field, data->params_all);
g_hash_table_foreach(action->extra, hash2field, data->params_all);
g_hash_table_foreach(rsc->parameters, hash2field, data->params_all);
g_hash_table_foreach(action->meta, hash2metafield, data->params_all);
filter_action_parameters(data->params_all, op_version);
data->digest_all_calc = calculate_operation_digest(data->params_all, op_version);
if (digest_restart) {
data->params_restart = copy_xml(data->params_all);
if (restart_list) {
filter_reload_parameters(data->params_restart, restart_list);
}
data->digest_restart_calc = calculate_operation_digest(data->params_restart, op_version);
}
if (digest_restart && strcmp(data->digest_restart_calc, digest_restart) != 0) {
data->rc = RSC_DIGEST_RESTART;
} else if (digest_all == NULL) {
/* it is unknown what the previous op digest was */
data->rc = RSC_DIGEST_UNKNOWN;
} else if (strcmp(digest_all, data->digest_all_calc) != 0) {
data->rc = RSC_DIGEST_ALL;
}
g_hash_table_insert(node->details->digest_cache, strdup(op_id), data);
g_hash_table_destroy(local_rsc_params);
pe_free_action(action);
return data;
}
const char *rsc_printable_id(resource_t *rsc)
{
if (is_not_set(rsc->flags, pe_rsc_unique)) {
return ID(rsc->xml);
}
return rsc->id;
}
gboolean
is_baremetal_remote_node(node_t *node)
{
if (node->details->remote_rsc && (node->details->remote_rsc->container == FALSE)) {
return TRUE;
}
return FALSE;
}
gboolean
is_container_remote_node(node_t *node)
{
if (node->details->remote_rsc && node->details->remote_rsc->container) {
return TRUE;
}
return FALSE;
}
gboolean
is_remote_node(node_t *node)
{
if (node->details->remote_rsc) {
return TRUE;
}
return FALSE;
}
resource_t *
rsc_contains_remote_node(pe_working_set_t * data_set, resource_t *rsc)
{
if (is_set(data_set->flags, pe_flag_have_remote_nodes) == FALSE) {
return NULL;
}
if (rsc->fillers) {
GListPtr gIter = NULL;
for (gIter = rsc->fillers; gIter != NULL; gIter = gIter->next) {
resource_t *filler = (resource_t *) gIter->data;
if (filler->is_remote_node) {
return filler;
}
}
}
return NULL;
}
gboolean
xml_contains_remote_node(xmlNode *xml)
{
const char *class = crm_element_value(xml, XML_AGENT_ATTR_CLASS);
const char *provider = crm_element_value(xml, XML_AGENT_ATTR_PROVIDER);
const char *agent = crm_element_value(xml, XML_ATTR_TYPE);
if (safe_str_eq(agent, "remote") && safe_str_eq(provider, "pacemaker") && safe_str_eq(class, "ocf")) {
return TRUE;
}
return FALSE;
}
diff --git a/pengine/allocate.c b/pengine/allocate.c
index d7d94a04e9..4e56354ee0 100644
--- a/pengine/allocate.c
+++ b/pengine/allocate.c
@@ -1,2499 +1,2503 @@
/*
* 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>
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,
},
{
clone_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,
},
{
master_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,
gboolean active_here, pe_working_set_t * data_set)
{
int attr_lpc = 0;
gboolean force_restart = FALSE;
gboolean delete_resource = FALSE;
gboolean changed = 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;
}
changed = TRUE;
if (active_here) {
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;
} else if (changed) {
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, strdup(key), RSC_CANCEL, active_node, FALSE, TRUE, data_set);
free(cancel->task);
free(cancel->cancel_task);
cancel->task = strdup(RSC_CANCEL);
cancel->cancel_task = strdup(task);
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;
const op_digest_cache_t *digest_data = NULL;
gboolean did_change = FALSE;
const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK);
const char *op_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");
if (interval > 0) {
xmlNode *op_match = NULL;
/* we need to reconstruct the key because of the way we used to construct resource IDs */
key = generate_op_key(rsc->id, task, interval);
pe_rsc_trace(rsc, "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) {
pe_rsc_debug(rsc, "Orphan action detected: %s on %s", key, active_node->details->uname);
free(key);
return TRUE;
}
free(key);
key = NULL;
}
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;
}
digest_data = rsc_action_digest_cmp(rsc, xml_op, active_node, data_set);
op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION);
/* Changes that force a restart */
if (digest_data->rc == RSC_DIGEST_RESTART) {
const char *digest_restart = crm_element_value(xml_op, XML_LRM_ATTR_RESTART_DIGEST);
did_change = TRUE;
key = generate_op_key(rsc->id, task, interval);
crm_log_xml_info(digest_data->params_restart, "params:restart");
pe_rsc_info(rsc, "Parameters to %s on %s changed: was %s vs. now %s (restart:%s) %s",
key, active_node->details->uname,
crm_str(digest_restart), digest_data->digest_restart_calc,
op_version, crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC));
custom_action(rsc, key, task, NULL, FALSE, TRUE, data_set);
} else if ((digest_data->rc == RSC_DIGEST_ALL) || (digest_data->rc == RSC_DIGEST_UNKNOWN)) {
/* Changes that can potentially be handled by a reload */
const char *digest_restart = crm_element_value(xml_op, XML_LRM_ATTR_RESTART_DIGEST);
const char *digest_all = crm_element_value(xml_op, XML_LRM_ATTR_OP_DIGEST);
did_change = TRUE;
crm_log_xml_info(digest_data->params_all, "params:reload");
key = generate_op_key(rsc->id, task, interval);
pe_rsc_info(rsc, "Parameters to %s on %s changed: was %s vs. now %s (reload:%s) %s",
key, active_node->details->uname,
crm_str(digest_all), digest_data->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) {
pe_rsc_trace(rsc, "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 {
pe_rsc_trace(rsc, "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);
}
}
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)) {
resource_t *parent = uber_parent(rsc);
if(parent == NULL
|| parent->variant < pe_clone
|| is_set(parent->flags, pe_rsc_unique)) {
pe_rsc_trace(rsc, "Skipping param check for %s and deleting: orphan", rsc->id);
DeleteRsc(rsc, node, FALSE, data_set);
} else {
pe_rsc_trace(rsc, "Skipping param check for %s (orphan clone)", rsc->id);
}
return;
} else if (pe_find_node_id(rsc->running_on, node->details->id) == NULL) {
if (check_rsc_parameters(rsc, node, rsc_entry, FALSE, data_set)) {
DeleteRsc(rsc, node, FALSE, data_set);
}
pe_rsc_trace(rsc, "Skipping param check for %s: no longer active on %s",
rsc->id, node->details->uname);
return;
}
pe_rsc_trace(rsc, "Processing %s on %s", rsc->id, node->details->uname);
if (check_rsc_parameters(rsc, node, rsc_entry, TRUE, 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(rsc->flags, pe_rsc_maintenance) || node->details->maintenance)) {
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(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 (renamed_clones && rsc->clone_name && strstr(rsc->clone_name, id)) {
match = TRUE;
}
} else {
if (strcmp(rsc->id, 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;
/* Still need to check actions for a maintenance node to cancel existing monitor operations */
} else if (can_run_resources(node) == FALSE && node->details->maintenance == 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;
if (rsc->variant != pe_native) {
continue;
}
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 gboolean
failcount_clear_action_exists(node_t * node, resource_t * rsc)
{
gboolean rc = FALSE;
char *key = crm_concat(rsc->id, CRM_OP_CLEAR_FAILCOUNT, '_');
GListPtr list = find_actions_exact(rsc->actions, key, node);
if (list) {
rc = TRUE;
}
g_list_free(list);
free(key);
return rc;
}
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);
pe_rsc_debug(sticky_rsc, "Resource %s: preferring current location"
" (node=%s, weight=%d)", sticky_rsc->id,
node->details->uname, rsc->stickiness);
} else {
GHashTableIter iter;
node_t *nIter = NULL;
pe_rsc_debug(rsc, "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);
}
}
}
/* only check failcount here if a failcount clear action
* has not already been placed for this resource on the node.
* There is no sense in potentially forcing the rsc from this
* node if the failcount is being reset anyway. */
if (failcount_clear_action_exists(node, rsc) == FALSE) {
fail_count = get_failcount_all(node, rsc, NULL, data_set);
}
if (fail_count > 0 && rsc->migration_threshold != 0) {
if (is_not_set(rsc->flags, pe_rsc_unique)) {
failed = uber_parent(rsc);
}
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_info("%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 = NULL;
if (safe_str_eq(action, RSC_STOP) && g_list_length(rsc->running_on) == 1) {
node_t *node = (node_t *) rsc->running_on->data;
/* Stop actions on nodes that are shutting down do not need to wait for probes to complete
* Doing so prevents node shutdown in the presence of nodes that are coming up
* The purpose of waiting is to not stop resources until we know for sure the
* intended destination is able to take them
*/
if (node && node->details->shutdown) {
crm_debug("Skipping %s before %s_%s_0 due to %s shutdown",
probe_complete->uuid, rsc->id, action, node->details->uname);
return;
}
}
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;
action_t *probe_cluster_nodes_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 (is_remote_node(node) && node->details->shutdown) {
/* Don't try and probe a remote node we're shutting down.
* It causes constraint conflicts to try and run any sort of action
* other that 'stop' on resources living within a remote-node when
* it is being shutdown. */
continue;
} else if (is_container_remote_node(node)) {
/* TODO enable container node probes once ordered probing is implemented. */
continue;
} else if (probe_complete == NULL) {
probe_complete = get_pseudo_op(CRM_OP_PROBED, data_set);
if (is_set(data_set->flags, pe_flag_have_remote_nodes)) {
probe_cluster_nodes_complete = get_pseudo_op(CRM_OP_NODES_PROBED, data_set);
}
}
if (probed != NULL && crm_is_true(probed) == FALSE) {
action_t *probe_op = custom_action(NULL, g_strdup_printf("%s-%s", CRM_OP_REPROBE, node->details->uname),
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, g_strdup_printf("%s-%s", CRM_OP_PROBED, node->details->uname),
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);
}
if (is_remote_node(node)) {
order_actions(probe_node_complete, probe_complete,
pe_order_runnable_left /*|pe_order_implies_then */ );
} else if (probe_cluster_nodes_complete == NULL) {
order_actions(probe_node_complete, probe_complete,
pe_order_runnable_left /*|pe_order_implies_then */ );
} else {
order_actions(probe_node_complete, probe_cluster_nodes_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);
if (rsc->is_remote_node || rsc_contains_remote_node(data_set, rsc)) {
update_action_flags(probe_cluster_nodes_complete, pe_action_optional | pe_action_clear);
/* allow remote connection resources and resources
* containing remote connection resources to run after all
* cluster nodes are probed */
wait_for_probe(rsc, RSC_START, probe_cluster_nodes_complete, data_set);
} else {
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;
if (rsc->is_remote_node || rsc_contains_remote_node(data_set, rsc)) {
/* allow remote connection resources and any resources containing
* remote connection resources to run after cluster nodes are probed.*/
wait_for_probe(rsc, RSC_STOP, probe_cluster_nodes_complete, data_set);
} else {
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_ping) {
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);
if (node != NULL) {
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);
if (node != NULL) {
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;
}
static void
allocate_resources(pe_working_set_t * data_set)
{
GListPtr gIter = data_set->resources;
if (is_set(data_set->flags, pe_flag_have_remote_nodes)) {
/* Force remote connection resources to be allocated first. This
* also forces any colocation dependencies to be allocated as well */
for (gIter = data_set->resources; gIter != NULL; gIter = gIter->next) {
resource_t *rsc = (resource_t *) gIter->data;
if (rsc->is_remote_node == FALSE) {
continue;
}
pe_rsc_trace(rsc, "Allocating: %s", rsc->id);
rsc->cmds->allocate(rsc, NULL, data_set);
}
}
/* now do the rest of the resources */
for (gIter = data_set->resources; gIter != NULL; gIter = gIter->next) {
resource_t *rsc = (resource_t *) gIter->data;
if (rsc->is_remote_node == TRUE) {
continue;
}
pe_rsc_trace(rsc, "Allocating: %s", rsc->id);
rsc->cmds->allocate(rsc, NULL, data_set);
}
}
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 */
allocate_resources(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_resources(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;
}
action_t *
pe_fence_op(node_t * node, const char *op, pe_working_set_t * data_set)
{
char *key = NULL;
action_t *stonith_op = NULL;
if(op == NULL) {
op = data_set->stonith_action;
}
key = g_strdup_printf("%s-%s-%s", CRM_OP_FENCE, node->details->uname, op);
+ if(data_set->singletons) {
+ stonith_op = g_hash_table_lookup(data_set->singletons, key);
+ }
+
if(stonith_op == NULL) {
stonith_op = custom_action(NULL, key, 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", op);
}
return stonith_op;
}
/*
* 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 *all_stopped = get_pseudo_op(ALL_STOPPED, data_set);
action_t *done = get_pseudo_op(STONITH_DONE, data_set);
gboolean need_stonith = TRUE;
GListPtr gIter = data_set->nodes;
crm_trace("Processing fencing and shutdown cases");
if (any_managed_resources(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;
/* remote-nodes associated with a container resource (such as a vm) are not fenced */
if (is_container_remote_node(node)) {
continue;
}
stonith_op = NULL;
if (need_stonith && node->details->unclean && pe_can_fence(data_set, node)) {
pe_warn("Scheduling Node %s for STONITH", node->details->uname);
stonith_op = pe_fence_op(node, NULL, data_set);
stonith_constraints(node, stonith_op, data_set);
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 &&
/* TODO define what a shutdown op means for a baremetal remote node.
* For now we do not send shutdown operations for remote nodes, but
* if we can come up with a good use for this in the future, we will. */
is_remote_node(node) == FALSE) {
action_t *down_op = NULL;
crm_notice("Scheduling Node %s for shutdown", node->details->uname);
down_op = custom_action(NULL, g_strdup_printf("%s-%s", CRM_OP_SHUTDOWN, node->details->uname),
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 gIter = 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);
for (gIter = data_set->actions; gIter != NULL; gIter = gIter->next) {
action_t *node_stop = (action_t *) gIter->data;
if (safe_str_neq(CRM_OP_SHUTDOWN, node_stop->task)) {
continue;
} else 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);
}
}
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(done, all_stopped, pe_order_implies_then);
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) {
pe_rsc_trace(rsc, "No RH-Side (%s/%s) found for constraint..."
" ignoring", rsc->id, order->rh_action_task);
if (lh_action) {
pe_rsc_trace(rsc, "LH-Side was: %s", lh_action->uuid);
}
return;
}
if (lh_action && lh_action->rsc == rsc && is_set(lh_action->flags, pe_action_dangle)) {
pe_rsc_trace(rsc, "Detected dangling operation %s -> %s", lh_action->uuid,
order->rh_action_task);
clear_bit(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_eq(op_type, RSC_STOP)) {
free(key);
pe_rsc_trace(lh_rsc, "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);
} else if (lh_rsc->fns->state(lh_rsc, TRUE) == RSC_ROLE_SLAVE && safe_str_eq(op_type, RSC_DEMOTE)) {
free(key);
pe_rsc_trace(lh_rsc, "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);
} else {
pe_rsc_trace(lh_rsc, "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);
}
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);
static void
apply_remote_node_ordering(pe_working_set_t *data_set)
{
GListPtr gIter = data_set->actions;
if (is_set(data_set->flags, pe_flag_have_remote_nodes) == FALSE) {
return;
}
for (; gIter != NULL; gIter = gIter->next) {
action_t *action = (action_t *) gIter->data;
resource_t *remote_rsc = NULL;
resource_t *container = NULL;
if (action->node == NULL ||
is_remote_node(action->node) == FALSE ||
action->rsc == NULL ||
is_set(action->flags, pe_action_pseudo)) {
continue;
}
remote_rsc = action->node->details->remote_rsc;
container = remote_rsc->container;
if (safe_str_eq(action->task, "monitor") ||
safe_str_eq(action->task, "start") ||
safe_str_eq(action->task, "promote") ||
safe_str_eq(action->task, CRM_OP_LRM_REFRESH) ||
safe_str_eq(action->task, CRM_OP_CLEAR_FAILCOUNT) ||
safe_str_eq(action->task, "delete")) {
custom_action_order(remote_rsc,
generate_op_key(remote_rsc->id, RSC_START, 0),
NULL,
action->rsc,
NULL,
action,
pe_order_implies_then | pe_order_runnable_left,
data_set);
} else if (safe_str_eq(action->task, "demote")) {
/* If the connection is being torn down, we don't want
* to build a constraint between a resource's demotion and
* the connection resource starting... because the connection
* resource can not start. The connection might already be up,
* but the START action would not be allowed which in turn would
* block the demotion of any resournces living in the remote-node.
*
* In this case, only build the constraint between the demotion and
* the connection's stop action. This allows the connection and all the
* resources within the remote-node to be torn down properly. */
if (remote_rsc->next_role == RSC_ROLE_STOPPED) {
custom_action_order(action->rsc,
NULL,
action,
remote_rsc,
generate_op_key(remote_rsc->id, RSC_STOP, 0),
NULL,
pe_order_implies_first,
data_set);
} else {
custom_action_order(remote_rsc,
generate_op_key(remote_rsc->id, RSC_START, 0),
NULL,
action->rsc,
NULL,
action,
pe_order_implies_then | pe_order_runnable_left,
data_set);
}
} else if (safe_str_eq(action->task, "stop") &&
container &&
is_set(container->flags, pe_rsc_failed)) {
/* when the container representing a remote node fails, the stop
* action for all the resources living in that container is implied
* by the container stopping. This is similar to how fencing operations
* work for cluster nodes. */
pe_set_action_bit(action, pe_action_pseudo);
custom_action_order(container,
generate_op_key(container->id, RSC_STOP, 0),
NULL,
action->rsc,
NULL,
action,
pe_order_implies_then | pe_order_runnable_left,
data_set);
} else if (safe_str_eq(action->task, "stop")) {
custom_action_order(action->rsc,
NULL,
action,
remote_rsc,
generate_op_key(remote_rsc->id, RSC_STOP, 0),
NULL,
pe_order_implies_first,
data_set);
}
}
}
gboolean
stage7(pe_working_set_t * data_set)
{
GListPtr gIter = NULL;
apply_remote_node_ordering(data_set);
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 reloads");
gIter = data_set->resources;
for (; gIter != NULL; gIter = gIter->next) {
resource_t *rsc = (resource_t *) gIter->data;
rsc_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 = strdup(" ");
}
if (node_list) {
*node_list = 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);
*rsc_list = 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);
*node_list = 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) {
pe_rsc_trace(rsc, "Op=%p confirm=%p", op, confirm);
return NULL;
}
CRM_CHECK(node != NULL, return NULL);
if (node->details->online == FALSE) {
pe_rsc_trace(rsc, "Skipping notification for %s: node offline", rsc->id);
return NULL;
} else if (is_set(op->flags, pe_action_runnable) == FALSE) {
pe_rsc_trace(rsc, "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");
pe_rsc_trace(rsc, "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 */
pe_rsc_trace(rsc, "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")) {
pe_rsc_trace(rsc, "Skipping %s: interval", mon->uuid);
continue;
} else if (safe_str_eq(mon->task, RSC_CANCEL)) {
pe_rsc_trace(rsc, "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);
add_hash_param(n_data->pre->meta, "notify_key_type", "pre");
add_hash_param(n_data->pre->meta, "notify_key_operation", start->task);
/* 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);
add_hash_param(n_data->pre_done->meta, "notify_key_type", "confirmed-pre");
add_hash_param(n_data->pre_done->meta, "notify_key_operation", start->task);
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);
add_hash_param(n_data->post->meta, "notify_key_type", "post");
add_hash_param(n_data->post->meta, "notify_key_operation", end->task);
/* 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", "post");
add_hash_param(n_data->post_done->meta, "notify_operation", n_data->action);
add_hash_param(n_data->post_done->meta, "notify_key_type", "confirmed-post");
add_hash_param(n_data->post_done->meta, "notify_key_operation", end->task);
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;
}
pe_rsc_trace(rsc, "%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, strdup("notify_stop_resource"), rsc_list);
g_hash_table_insert(n_data->keys, 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, strdup("notify_start_resource"), rsc_list);
g_hash_table_insert(n_data->keys, 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, strdup("notify_demote_resource"), rsc_list);
g_hash_table_insert(n_data->keys, 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, strdup("notify_promote_resource"), rsc_list);
g_hash_table_insert(n_data->keys, 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, strdup("notify_active_resource"), rsc_list);
g_hash_table_insert(n_data->keys, 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, strdup("notify_slave_resource"), rsc_list);
g_hash_table_insert(n_data->keys, 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, strdup("notify_master_resource"), rsc_list);
g_hash_table_insert(n_data->keys, 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, 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;
}
}
}
pe_rsc_trace(rsc, "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;
}
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;
pe_rsc_trace(rsc, "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(action->rsc->flags, pe_rsc_maintenance)
&& is_not_set(action->flags, pe_action_optional)
&& is_not_set(action->flags, pe_action_runnable)
&& crm_str_eq(action->task, RSC_STOP, TRUE)
) {
/* Eventually we should just ignore the 'fence' case
* But for now its the best way to detect (in CTS) when
* CIB resource updates are being lost
*/
if (is_set(data_set->flags, pe_flag_have_quorum)
|| data_set->no_quorum_policy == no_quorum_ignore) {
crm_crit("Cannot %s node '%s' because of %s:%s%s",
action->node->details->unclean ? "fence" : "shut down",
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);
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);
g_list_free_full(data_set->ticket_constraints, free);
data_set->ticket_constraints = NULL;
cleanup_calculations(data_set);
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Tue, Jul 8, 6:04 PM (1 d, 2 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2002453
Default Alt Text
(164 KB)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment