Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/include/crm/msg_xml.h b/include/crm/msg_xml.h
index 3842d6dbce..1d281b0e1d 100644
--- a/include/crm/msg_xml.h
+++ b/include/crm/msg_xml.h
@@ -1,317 +1,318 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef XML_TAGS__H
# define XML_TAGS__H
# define CIB_OPTIONS_FIRST "cib-bootstrap-options"
# define F_CRM_DATA "crm_xml"
# define F_CRM_TASK "crm_task"
# define F_CRM_HOST_TO "crm_host_to"
# define F_CRM_MSG_TYPE F_SUBTYPE
# define F_CRM_SYS_TO "crm_sys_to"
# define F_CRM_SYS_FROM "crm_sys_from"
# define F_CRM_HOST_FROM F_ORIG
# define F_CRM_REFERENCE XML_ATTR_REFERENCE
# define F_CRM_VERSION XML_ATTR_VERSION
# define F_CRM_ORIGIN "origin"
# define F_CRM_USER "crm_user"
# define F_CRM_JOIN_ID "join_id"
# define F_CRM_ELECTION_ID "election-id"
# define F_CRM_ELECTION_AGE_S "election-age-sec"
# define F_CRM_ELECTION_AGE_US "election-age-nano-sec"
# define F_CRM_ELECTION_OWNER "election-owner"
# define F_CRM_TGRAPH "crm-tgraph"
# define F_CRM_TGRAPH_INPUT "crm-tgraph-in"
/*---- Common tags/attrs */
# define XML_DIFF_MARKER "__crm_diff_marker__"
# define XML_ATTR_TAGNAME F_XML_TAGNAME
# define XML_TAG_CIB "cib"
# define XML_TAG_FAILED "failed"
# define XML_ATTR_CRM_VERSION "crm_feature_set"
# define XML_ATTR_DIGEST "digest"
# define XML_ATTR_VALIDATION "validate-with"
# define XML_ATTR_QUORUM_PANIC "no-quorum-panic"
# define XML_ATTR_HAVE_QUORUM "have-quorum"
# define XML_ATTR_EXPECTED_VOTES "expected-quorum-votes"
# define XML_ATTR_GENERATION "epoch"
# define XML_ATTR_GENERATION_ADMIN "admin_epoch"
# define XML_ATTR_NUMUPDATES "num_updates"
# define XML_ATTR_TIMEOUT "timeout"
# define XML_ATTR_ORIGIN "crm-debug-origin"
# define XML_ATTR_TSTAMP "crm-timestamp"
# define XML_CIB_ATTR_WRITTEN "cib-last-written"
# define XML_ATTR_VERSION "version"
# define XML_ATTR_DESC "description"
# define XML_ATTR_ID "id"
# define XML_ATTR_IDREF "id-ref"
# define XML_ATTR_ID_LONG "long-id"
# define XML_ATTR_TYPE "type"
# define XML_ATTR_FILTER_TYPE "type-filter"
# define XML_ATTR_FILTER_ID "id-filter"
# define XML_ATTR_FILTER_PRIORITY "priority-filter"
# define XML_ATTR_VERBOSE "verbose"
# define XML_ATTR_OP "op"
# define XML_ATTR_DC "is_dc"
# define XML_ATTR_DC_UUID "dc-uuid"
# define XML_ATTR_UPDATE_ORIG "update-origin"
# define XML_ATTR_UPDATE_CLIENT "update-client"
# define XML_ATTR_UPDATE_USER "update-user"
# define XML_BOOLEAN_TRUE "true"
# define XML_BOOLEAN_FALSE "false"
# define XML_BOOLEAN_YES XML_BOOLEAN_TRUE
# define XML_BOOLEAN_NO XML_BOOLEAN_FALSE
# define XML_TAG_OPTIONS "options"
/*---- top level tags/attrs */
# define XML_MSG_TAG "crm_message"
# define XML_MSG_TAG_DATA "msg_data"
# define XML_ATTR_REQUEST "request"
# define XML_ATTR_RESPONSE "response"
# define XML_ATTR_UNAME "uname"
# define XML_ATTR_UUID "id"
# define XML_ATTR_REFERENCE "reference"
# define XML_FAIL_TAG_RESOURCE "failed_resource"
# define XML_FAILRES_ATTR_RESID "resource_id"
# define XML_FAILRES_ATTR_REASON "reason"
# define XML_FAILRES_ATTR_RESSTATUS "resource_status"
# define XML_CRM_TAG_PING "ping_response"
# define XML_PING_ATTR_STATUS "result"
# define XML_PING_ATTR_SYSFROM "crm_subsystem"
# define XML_TAG_FRAGMENT "cib_fragment"
# define XML_ATTR_RESULT "result"
# define XML_ATTR_SECTION "section"
# define XML_FAIL_TAG_CIB "failed_update"
# define XML_FAILCIB_ATTR_ID "id"
# define XML_FAILCIB_ATTR_OBJTYPE "object_type"
# define XML_FAILCIB_ATTR_OP "operation"
# define XML_FAILCIB_ATTR_REASON "reason"
/*---- CIB specific tags/attrs */
# define XML_CIB_TAG_SECTION_ALL "all"
# define XML_CIB_TAG_CONFIGURATION "configuration"
# define XML_CIB_TAG_STATUS "status"
# define XML_CIB_TAG_RESOURCES "resources"
# define XML_CIB_TAG_NODES "nodes"
# define XML_CIB_TAG_DOMAINS "domains"
# define XML_CIB_TAG_CONSTRAINTS "constraints"
# define XML_CIB_TAG_CRMCONFIG "crm_config"
# define XML_CIB_TAG_OPCONFIG "op_defaults"
# define XML_CIB_TAG_RSCCONFIG "rsc_defaults"
# define XML_CIB_TAG_ACLS "acls"
# define XML_CIB_TAG_STATE "node_state"
# define XML_CIB_TAG_NODE "node"
# define XML_CIB_TAG_DOMAIN "domain"
# define XML_CIB_TAG_CONSTRAINT "constraint"
# define XML_CIB_TAG_NVPAIR "nvpair"
# define XML_CIB_TAG_PROPSET "cluster_property_set"
# define XML_TAG_ATTR_SETS "instance_attributes"
# define XML_TAG_META_SETS "meta_attributes"
# define XML_TAG_ATTRS "attributes"
# define XML_TAG_PARAMS "parameters"
# define XML_TAG_PARAM "param"
# define XML_TAG_UTILIZATION "utilization"
# define XML_TAG_RESOURCE_REF "resource_ref"
# define XML_CIB_TAG_RESOURCE "primitive"
# define XML_CIB_TAG_GROUP "group"
# define XML_CIB_TAG_INCARNATION "clone"
# define XML_CIB_TAG_MASTER "master"
# define XML_CIB_TAG_RSC_TEMPLATE "template"
# define XML_RSC_ATTR_RESTART "restart-type"
# define XML_RSC_ATTR_ORDERED "ordered"
# define XML_RSC_ATTR_INTERLEAVE "interleave"
# define XML_RSC_ATTR_INCARNATION "clone"
# define XML_RSC_ATTR_INCARNATION_MAX "clone-max"
# define XML_RSC_ATTR_INCARNATION_NODEMAX "clone-node-max"
# define XML_RSC_ATTR_MASTER_MAX "master-max"
# define XML_RSC_ATTR_MASTER_NODEMAX "master-node-max"
# define XML_RSC_ATTR_STATE "clone-state"
# define XML_RSC_ATTR_MANAGED "is-managed"
# define XML_RSC_ATTR_TARGET_ROLE "target-role"
# define XML_RSC_ATTR_UNIQUE "globally-unique"
# define XML_RSC_ATTR_NOTIFY "notify"
# define XML_RSC_ATTR_STICKINESS "resource-stickiness"
# define XML_RSC_ATTR_FAIL_STICKINESS "migration-threshold"
# define XML_RSC_ATTR_FAIL_TIMEOUT "failure-timeout"
# define XML_RSC_ATTR_MULTIPLE "multiple-active"
# define XML_RSC_ATTR_PRIORITY "priority"
# define XML_OP_ATTR_ON_FAIL "on-fail"
# define XML_OP_ATTR_START_DELAY "start-delay"
# define XML_OP_ATTR_ALLOW_MIGRATE "allow-migrate"
# define XML_OP_ATTR_ORIGIN "interval-origin"
# define XML_OP_ATTR_PENDING "record-pending"
# define XML_CIB_TAG_LRM "lrm"
# define XML_LRM_TAG_RESOURCES "lrm_resources"
# define XML_LRM_TAG_RESOURCE "lrm_resource"
# define XML_LRM_TAG_AGENTS "lrm_agents"
# define XML_LRM_TAG_AGENT "lrm_agent"
# define XML_LRM_TAG_RSC_OP "lrm_rsc_op"
# define XML_AGENT_ATTR_CLASS "class"
# define XML_AGENT_ATTR_PROVIDER "provider"
# define XML_LRM_TAG_ATTRIBUTES "attributes"
# define XML_CIB_ATTR_REPLACE "replace"
# define XML_CIB_ATTR_SOURCE "source"
# define XML_CIB_ATTR_HEALTH "health"
# define XML_CIB_ATTR_WEIGHT "weight"
# define XML_CIB_ATTR_PRIORITY "priority"
# define XML_CIB_ATTR_CLEAR "clear_on"
# define XML_CIB_ATTR_SOURCE "source"
# define XML_CIB_ATTR_JOINSTATE "join"
# define XML_CIB_ATTR_EXPSTATE "expected"
# define XML_CIB_ATTR_INCCM "in_ccm"
# define XML_CIB_ATTR_CRMDSTATE "crmd"
# define XML_CIB_ATTR_HASTATE "ha"
# define XML_CIB_ATTR_SHUTDOWN "shutdown"
# define XML_CIB_ATTR_STONITH "stonith"
# define XML_LRM_ATTR_INTERVAL "interval"
# define XML_LRM_ATTR_TASK "operation"
# define XML_LRM_ATTR_TASK_KEY "operation_key"
# define XML_LRM_ATTR_TARGET "on_node"
# define XML_LRM_ATTR_TARGET_UUID "on_node_uuid"
# define XML_LRM_ATTR_RSCID "rsc-id"
# define XML_LRM_ATTR_OPSTATUS "op-status"
# define XML_LRM_ATTR_RC "rc-code"
# define XML_LRM_ATTR_CALLID "call-id"
# define XML_LRM_ATTR_OP_DIGEST "op-digest"
# define XML_LRM_ATTR_OP_RESTART "op-force-restart"
# define XML_LRM_ATTR_RESTART_DIGEST "op-restart-digest"
# define XML_LRM_ATTR_MIGRATE_SOURCE "migrate_source"
# define XML_LRM_ATTR_MIGRATE_TARGET "migrate_target"
# define XML_TAG_GRAPH "transition_graph"
# define XML_GRAPH_TAG_RSC_OP "rsc_op"
# define XML_GRAPH_TAG_PSEUDO_EVENT "pseudo_event"
# define XML_GRAPH_TAG_CRM_EVENT "crm_event"
# define XML_TAG_RULE "rule"
# define XML_RULE_ATTR_SCORE "score"
# define XML_RULE_ATTR_SCORE_ATTRIBUTE "score-attribute"
# define XML_RULE_ATTR_SCORE_MANGLED "score-attribute-mangled"
# define XML_RULE_ATTR_ROLE "role"
# define XML_RULE_ATTR_RESULT "result"
# define XML_RULE_ATTR_BOOLEAN_OP "boolean-op"
# define XML_TAG_EXPRESSION "expression"
# define XML_EXPR_ATTR_ATTRIBUTE "attribute"
# define XML_EXPR_ATTR_OPERATION "operation"
# define XML_EXPR_ATTR_VALUE "value"
# define XML_EXPR_ATTR_TYPE "type"
# define XML_CONS_TAG_RSC_DEPEND "rsc_colocation"
# define XML_CONS_TAG_RSC_ORDER "rsc_order"
# define XML_CONS_TAG_RSC_LOCATION "rsc_location"
# define XML_CONS_TAG_RSC_TICKET "rsc_ticket"
# define XML_CONS_TAG_RSC_SET "resource_set"
# define XML_CONS_ATTR_SYMMETRICAL "symmetrical"
# define XML_COLOC_ATTR_SOURCE "rsc"
# define XML_COLOC_ATTR_SOURCE_ROLE "rsc-role"
# define XML_COLOC_ATTR_TARGET "with-rsc"
# define XML_COLOC_ATTR_TARGET_ROLE "with-rsc-role"
# define XML_COLOC_ATTR_NODE_ATTR "node-attribute"
# define XML_COLOC_ATTR_SOURCE_INSTANCE "rsc-instance"
# define XML_COLOC_ATTR_TARGET_INSTANCE "with-rsc-instance"
# define XML_ORDER_ATTR_FIRST "first"
# define XML_ORDER_ATTR_THEN "then"
# define XML_ORDER_ATTR_FIRST_ACTION "first-action"
# define XML_ORDER_ATTR_THEN_ACTION "then-action"
# define XML_ORDER_ATTR_FIRST_INSTANCE "first-instance"
# define XML_ORDER_ATTR_THEN_INSTANCE "then-instance"
# define XML_ORDER_ATTR_KIND "kind"
# define XML_TICKET_ATTR_TICKET "ticket"
# define XML_TICKET_ATTR_LOSS_POLICY "loss-policy"
# define XML_NVPAIR_ATTR_NAME "name"
# define XML_NVPAIR_ATTR_VALUE "value"
# define XML_NODE_ATTR_STATE "state"
# define XML_CONFIG_ATTR_DC_DEADTIME "dc-deadtime"
# define XML_CONFIG_ATTR_ELECTION_FAIL "election-timeout"
# define XML_CONFIG_ATTR_FORCE_QUIT "shutdown-escalation"
# define XML_CONFIG_ATTR_RECHECK "cluster-recheck-interval"
# define XML_CIB_TAG_GENERATION_TUPPLE "generation_tuple"
# define XML_ATTR_TRANSITION_MAGIC "transition-magic"
# define XML_ATTR_TRANSITION_KEY "transition-key"
# define XML_ATTR_TE_NOWAIT "op_no_wait"
# define XML_ATTR_TE_TARGET_RC "op_target_rc"
# define XML_ATTR_TE_ALLOWFAIL "op_allow_fail"
# define XML_ATTR_LRM_PROBE "lrm-is-probe"
# define XML_TAG_TRANSIENT_NODEATTRS "transient_attributes"
# define XML_TAG_DIFF_ADDED "diff-added"
# define XML_TAG_DIFF_REMOVED "diff-removed"
# define XML_ACL_TAG_USER "acl_user"
# define XML_ACL_TAG_ROLE "acl_role"
# define XML_ACL_TAG_ROLE_REF "role_ref"
# define XML_ACL_TAG_READ "read"
# define XML_ACL_TAG_WRITE "write"
# define XML_ACL_TAG_DENY "deny"
# define XML_ACL_ATTR_REF "ref"
# define XML_ACL_ATTR_TAG "tag"
# define XML_ACL_ATTR_XPATH "xpath"
# define XML_ACL_ATTR_ATTRIBUTE "attribute"
-# define XML_CIB_TAG_TICKETS "tickets"
+# define XML_CIB_TAG_TICKETS "tickets"
+# define XML_CIB_TAG_TICKET_STATE "ticket_state"
# define XML_TAG_FENCING_TOPOLOGY "fencing-topology"
# define XML_TAG_FENCING_LEVEL "fencing-level"
# define XML_ATTR_STONITH_INDEX "index"
# define XML_ATTR_STONITH_TARGET "target"
# define XML_ATTR_STONITH_DEVICES "devices"
# include <crm/common/xml.h>
# define ID(x) crm_element_value(x, XML_ATTR_ID)
# define INSTANCE(x) crm_element_value(x, XML_CIB_ATTR_INSTANCE)
# define TSTAMP(x) crm_element_value(x, XML_ATTR_TSTAMP)
# define TYPE(x) crm_element_name(x)
# define NAME(x) crm_element_value(x, XML_NVPAIR_ATTR_NAME)
# define VALUE(x) crm_element_value(x, XML_NVPAIR_ATTR_VALUE)
#endif
diff --git a/include/crm/pengine/status.h b/include/crm/pengine/status.h
index debc770a64..f6e0e6245e 100644
--- a/include/crm/pengine/status.h
+++ b/include/crm/pengine/status.h
@@ -1,354 +1,356 @@
/*
* 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 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
};
enum pe_restart {
pe_restart_restart,
pe_restart_ignore
};
enum pe_find {
pe_find_renamed = 0x001,
pe_find_partial = 0x002,
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
typedef struct pe_working_set_s {
xmlNode *input;
ha_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;
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;
} pe_working_set_t;
struct node_shared_s {
const char *id;
const char *uname;
gboolean online;
gboolean standby;
gboolean standby_onfail;
gboolean pending;
gboolean unclean;
gboolean shutdown;
gboolean expected_up;
gboolean is_dc;
int num_resources;
GListPtr running_rsc; /* resource_t* */
GListPtr allocated_rsc; /* resource_t* */
GHashTable *attrs; /* char* => char* */
enum node_type type;
GHashTable *utilization;
};
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_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_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_failure_ignored 0x01000000ULL
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_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;
char *long_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;
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* */
};
struct pe_action_s {
int id;
int priority;
resource_t *rsc;
node_t *node;
xmlNode *op_entry;
char *task;
char *uuid;
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* */
};
typedef struct notify_data_s {
GHashTable *keys;
const char *action;
action_t *pre;
action_t *post;
action_t *pre_done;
action_t *post_done;
GListPtr active; /* notify_entry_t* */
GListPtr inactive; /* notify_entry_t* */
GListPtr start; /* notify_entry_t* */
GListPtr stop; /* notify_entry_t* */
GListPtr demote; /* notify_entry_t* */
GListPtr promote; /* notify_entry_t* */
GListPtr master; /* notify_entry_t* */
GListPtr slave; /* notify_entry_t* */
} notify_data_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_implies_first = 0x10, /* If 'first' is required, ensure 'then' is too */
pe_order_implies_then = 0x20, /* If 'then' is required, ensure 'first' is too */
pe_order_runnable_left = 0x100, /* 'then' requires 'first' to be 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_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;
};
gboolean cluster_status(pe_working_set_t * data_set);
extern void set_working_set_defaults(pe_working_set_t * data_set);
extern void cleanup_calculations(pe_working_set_t * data_set);
extern resource_t *pe_find_resource(GListPtr rsc_list, const char *id_rh);
extern node_t *pe_find_node(GListPtr node_list, const char *uname);
extern node_t *pe_find_node_id(GListPtr node_list, const char *id);
extern GListPtr find_operations(const char *rsc, const char *node, gboolean active_filter,
pe_working_set_t * data_set);
#endif
diff --git a/lib/pengine/unpack.c b/lib/pengine/unpack.c
index c2d8853e9f..4a8a20fc86 100644
--- a/lib/pengine/unpack.c
+++ b/lib/pengine/unpack.c
@@ -1,2306 +1,2395 @@
/*
* 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 <lrm/lrm_api.h>
#include <glib.h>
#include <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/msg.h>
#include <crm/common/util.h>
#include <crm/pengine/status.h>
#include <crm/pengine/rules.h>
#include <utils.h>
#include <unpack.h>
CRM_TRACE_INIT_DATA(pe_status);
#define set_config_flag(data_set, option, flag) do { \
const char *tmp = pe_pref(data_set->config_hash, option); \
if(tmp) { \
if(crm_is_true(tmp)) { \
set_bit_inplace(data_set->flags, flag); \
} else { \
clear_bit_inplace(data_set->flags, flag); \
} \
} \
} while(0)
gboolean unpack_rsc_op(resource_t * rsc, node_t * node, xmlNode * xml_op, GListPtr next,
enum action_fail_response *failed, pe_working_set_t * data_set);
static void
pe_fence_node(pe_working_set_t * data_set, node_t * node, const char *reason)
{
CRM_CHECK(node, return);
if (node->details->unclean == FALSE) {
if (is_set(data_set->flags, pe_flag_stonith_enabled)) {
crm_warn("Node %s will be fenced %s", node->details->uname, reason);
} else {
crm_warn("Node %s is unclean %s", node->details->uname, reason);
}
}
node->details->unclean = TRUE;
}
gboolean
unpack_config(xmlNode * config, pe_working_set_t * data_set)
{
const char *value = NULL;
GHashTable *config_hash =
g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str);
data_set->config_hash = config_hash;
unpack_instance_attributes(data_set->input, config, XML_CIB_TAG_PROPSET, NULL, config_hash,
CIB_OPTIONS_FIRST, FALSE, data_set->now);
verify_pe_options(data_set->config_hash);
set_config_flag(data_set, "enable-startup-probes", pe_flag_startup_probes);
crm_info("Startup probes: %s",
is_set(data_set->flags, pe_flag_startup_probes) ? "enabled" : "disabled (dangerous)");
value = pe_pref(data_set->config_hash, "stonith-timeout");
data_set->stonith_timeout = crm_get_msec(value);
crm_debug("STONITH timeout: %d", data_set->stonith_timeout);
set_config_flag(data_set, "stonith-enabled", pe_flag_stonith_enabled);
crm_debug("STONITH of failed nodes is %s",
is_set(data_set->flags, pe_flag_stonith_enabled) ? "enabled" : "disabled");
data_set->stonith_action = pe_pref(data_set->config_hash, "stonith-action");
crm_trace("STONITH will %s nodes", data_set->stonith_action);
set_config_flag(data_set, "stop-all-resources", pe_flag_stop_everything);
crm_debug("Stop all active resources: %s",
is_set(data_set->flags, pe_flag_stop_everything) ? "true" : "false");
set_config_flag(data_set, "symmetric-cluster", pe_flag_symmetric_cluster);
if (is_set(data_set->flags, pe_flag_symmetric_cluster)) {
crm_debug("Cluster is symmetric" " - resources can run anywhere by default");
}
value = pe_pref(data_set->config_hash, "default-resource-stickiness");
data_set->default_resource_stickiness = char2score(value);
crm_debug("Default stickiness: %d", data_set->default_resource_stickiness);
value = pe_pref(data_set->config_hash, "no-quorum-policy");
if (safe_str_eq(value, "ignore")) {
data_set->no_quorum_policy = no_quorum_ignore;
} else if (safe_str_eq(value, "freeze")) {
data_set->no_quorum_policy = no_quorum_freeze;
} else if (safe_str_eq(value, "suicide")) {
gboolean do_panic = FALSE;
crm_element_value_int(data_set->input, XML_ATTR_QUORUM_PANIC, &do_panic);
if (is_set(data_set->flags, pe_flag_stonith_enabled) == FALSE) {
crm_config_err
("Setting no-quorum-policy=suicide makes no sense if stonith-enabled=false");
}
if (do_panic && is_set(data_set->flags, pe_flag_stonith_enabled)) {
data_set->no_quorum_policy = no_quorum_suicide;
} else if (is_set(data_set->flags, pe_flag_have_quorum) == FALSE && do_panic == FALSE) {
crm_notice("Resetting no-quorum-policy to 'stop': The cluster has never had quorum");
data_set->no_quorum_policy = no_quorum_stop;
}
} else {
data_set->no_quorum_policy = no_quorum_stop;
}
switch (data_set->no_quorum_policy) {
case no_quorum_freeze:
crm_debug("On loss of CCM Quorum: Freeze resources");
break;
case no_quorum_stop:
crm_debug("On loss of CCM Quorum: Stop ALL resources");
break;
case no_quorum_suicide:
crm_notice("On loss of CCM Quorum: Fence all remaining nodes");
break;
case no_quorum_ignore:
crm_notice("On loss of CCM Quorum: Ignore");
break;
}
set_config_flag(data_set, "stop-orphan-resources", pe_flag_stop_rsc_orphans);
crm_trace("Orphan resources are %s",
is_set(data_set->flags, pe_flag_stop_rsc_orphans) ? "stopped" : "ignored");
set_config_flag(data_set, "stop-orphan-actions", pe_flag_stop_action_orphans);
crm_trace("Orphan resource actions are %s",
is_set(data_set->flags, pe_flag_stop_action_orphans) ? "stopped" : "ignored");
set_config_flag(data_set, "remove-after-stop", pe_flag_remove_after_stop);
crm_trace("Stopped resources are removed from the status section: %s",
is_set(data_set->flags, pe_flag_remove_after_stop) ? "true" : "false");
set_config_flag(data_set, "maintenance-mode", pe_flag_maintenance_mode);
crm_trace("Maintenance mode: %s",
is_set(data_set->flags, pe_flag_maintenance_mode) ? "true" : "false");
if (is_set(data_set->flags, pe_flag_maintenance_mode)) {
clear_bit(data_set->flags, pe_flag_is_managed_default);
} else {
set_config_flag(data_set, "is-managed-default", pe_flag_is_managed_default);
}
crm_trace("By default resources are %smanaged",
is_set(data_set->flags, pe_flag_is_managed_default) ? "" : "not ");
set_config_flag(data_set, "start-failure-is-fatal", pe_flag_start_failure_fatal);
crm_trace("Start failures are %s",
is_set(data_set->flags,
pe_flag_start_failure_fatal) ? "always fatal" : "handled by failcount");
node_score_red = char2score(pe_pref(data_set->config_hash, "node-health-red"));
node_score_green = char2score(pe_pref(data_set->config_hash, "node-health-green"));
node_score_yellow = char2score(pe_pref(data_set->config_hash, "node-health-yellow"));
crm_info("Node scores: 'red' = %s, 'yellow' = %s, 'green' = %s",
pe_pref(data_set->config_hash, "node-health-red"),
pe_pref(data_set->config_hash, "node-health-yellow"),
pe_pref(data_set->config_hash, "node-health-green"));
data_set->placement_strategy = pe_pref(data_set->config_hash, "placement-strategy");
crm_trace("Placement strategy: %s", data_set->placement_strategy);
return TRUE;
}
gboolean
unpack_nodes(xmlNode * xml_nodes, pe_working_set_t * data_set)
{
xmlNode *xml_obj = NULL;
node_t *new_node = NULL;
const char *id = NULL;
const char *uname = NULL;
const char *type = NULL;
const char *score = NULL;
gboolean unseen_are_unclean = TRUE;
const char *blind_faith = pe_pref(data_set->config_hash, "startup-fencing");
if (crm_is_true(blind_faith) == FALSE) {
unseen_are_unclean = FALSE;
crm_warn("Blind faith: not fencing unseen nodes");
}
for (xml_obj = __xml_first_child(xml_nodes); xml_obj != NULL; xml_obj = __xml_next(xml_obj)) {
if (crm_str_eq((const char *)xml_obj->name, XML_CIB_TAG_NODE, TRUE)) {
new_node = NULL;
id = crm_element_value(xml_obj, XML_ATTR_ID);
uname = crm_element_value(xml_obj, XML_ATTR_UNAME);
type = crm_element_value(xml_obj, XML_ATTR_TYPE);
score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
crm_trace("Processing node %s/%s", uname, id);
if (id == NULL) {
crm_config_err("Must specify id tag in <node>");
continue;
}
if (type == NULL) {
crm_config_err("Must specify type tag in <node>");
continue;
}
if (pe_find_node(data_set->nodes, uname) != NULL) {
crm_config_warn("Detected multiple node entries with uname=%s"
" - this is rarely intended", uname);
}
crm_malloc0(new_node, sizeof(node_t));
if (new_node == NULL) {
return FALSE;
}
new_node->weight = char2score(score);
new_node->fixed = FALSE;
crm_malloc0(new_node->details, sizeof(struct node_shared_s));
if (new_node->details == NULL) {
crm_free(new_node);
return FALSE;
}
crm_trace("Creaing node for entry %s/%s", uname, id);
new_node->details->id = id;
new_node->details->uname = uname;
new_node->details->type = node_ping;
new_node->details->online = FALSE;
new_node->details->shutdown = FALSE;
new_node->details->running_rsc = NULL;
new_node->details->attrs = g_hash_table_new_full(crm_str_hash, g_str_equal,
g_hash_destroy_str,
g_hash_destroy_str);
new_node->details->utilization =
g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str,
g_hash_destroy_str);
/* if(data_set->have_quorum == FALSE */
/* && data_set->no_quorum_policy == no_quorum_stop) { */
/* /\* start shutting resources down *\/ */
/* new_node->weight = -INFINITY; */
/* } */
if (is_set(data_set->flags, pe_flag_stonith_enabled) == FALSE
|| unseen_are_unclean == FALSE) {
/* blind faith... */
new_node->details->unclean = FALSE;
} else {
/* all nodes are unclean until we've seen their
* status entry
*/
new_node->details->unclean = TRUE;
}
if (type == NULL || safe_str_eq(type, "member")
|| safe_str_eq(type, NORMALNODE)) {
new_node->details->type = node_member;
}
add_node_attrs(xml_obj, new_node, FALSE, data_set);
unpack_instance_attributes(data_set->input, xml_obj, XML_TAG_UTILIZATION, NULL,
new_node->details->utilization, NULL, FALSE, data_set->now);
data_set->nodes = g_list_append(data_set->nodes, new_node);
crm_trace("Done with node %s", crm_element_value(xml_obj, XML_ATTR_UNAME));
}
}
return TRUE;
}
static void
g_hash_destroy_node_list(gpointer data)
{
GListPtr domain = data;
slist_basic_destroy(domain);
}
gboolean
unpack_domains(xmlNode * xml_domains, pe_working_set_t * data_set)
{
const char *id = NULL;
GListPtr domain = NULL;
xmlNode *xml_node = NULL;
xmlNode *xml_domain = NULL;
crm_info("Unpacking domains");
data_set->domains =
g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str,
g_hash_destroy_node_list);
for (xml_domain = __xml_first_child(xml_domains); xml_domain != NULL;
xml_domain = __xml_next(xml_domain)) {
if (crm_str_eq((const char *)xml_domain->name, XML_CIB_TAG_DOMAIN, TRUE)) {
domain = NULL;
id = crm_element_value(xml_domain, XML_ATTR_ID);
for (xml_node = __xml_first_child(xml_domain); xml_node != NULL;
xml_node = __xml_next(xml_node)) {
if (crm_str_eq((const char *)xml_node->name, XML_CIB_TAG_NODE, TRUE)) {
node_t *copy = NULL;
node_t *node = NULL;
const char *uname = crm_element_value(xml_node, "name");
const char *score = crm_element_value(xml_node, XML_RULE_ATTR_SCORE);
if (uname == NULL) {
crm_config_err("Invalid domain %s: Must specify id tag in <node>", id);
continue;
}
node = pe_find_node(data_set->nodes, uname);
if (node == NULL) {
node = pe_find_node_id(data_set->nodes, uname);
}
if (node == NULL) {
crm_config_warn("Invalid domain %s: Node %s does not exist", id, uname);
continue;
}
copy = node_copy(node);
copy->weight = char2score(score);
crm_debug("Adding %s to domain %s with score %s", node->details->uname, id,
score);
domain = g_list_prepend(domain, copy);
}
}
if (domain) {
crm_debug("Created domain %s with %d members", id, g_list_length(domain));
g_hash_table_replace(data_set->domains, crm_strdup(id), domain);
}
}
}
return TRUE;
}
static void
destroy_template_rsc_set(gpointer data)
{
xmlNode *rsc_set = data;
free_xml(rsc_set);
}
gboolean
unpack_resources(xmlNode * xml_resources, pe_working_set_t * data_set)
{
xmlNode *xml_obj = NULL;
data_set->template_rsc_sets = g_hash_table_new_full(
crm_str_hash, g_str_equal, g_hash_destroy_str, destroy_template_rsc_set);
for (xml_obj = __xml_first_child(xml_resources); xml_obj != NULL; xml_obj = __xml_next(xml_obj)) {
resource_t *new_rsc = NULL;
if (crm_str_eq((const char *)xml_obj->name, XML_CIB_TAG_RSC_TEMPLATE, TRUE)) {
const char *template_id = ID(xml_obj);
if (template_id && g_hash_table_lookup_extended(data_set->template_rsc_sets,
template_id, NULL, NULL) == FALSE) {
/* Record the template's ID for the knowledge of its existence anyway. */
g_hash_table_insert(data_set->template_rsc_sets, crm_strdup(template_id), NULL);
}
continue;
}
crm_trace("Beginning unpack... <%s id=%s... >", crm_element_name(xml_obj), ID(xml_obj));
if (common_unpack(xml_obj, &new_rsc, NULL, data_set)) {
data_set->resources = g_list_append(data_set->resources, new_rsc);
print_resource(LOG_DEBUG_3, "Added", new_rsc, FALSE);
} else {
crm_config_err("Failed unpacking %s %s",
crm_element_name(xml_obj), crm_element_value(xml_obj, XML_ATTR_ID));
if (new_rsc != NULL && new_rsc->fns != NULL) {
new_rsc->fns->free(new_rsc);
}
}
}
data_set->resources = g_list_sort(data_set->resources, sort_rsc_priority);
if (is_set(data_set->flags, pe_flag_stonith_enabled)
&& is_set(data_set->flags, pe_flag_have_stonith_resource) == FALSE) {
crm_config_err("Resource start-up disabled since no STONITH resources have been defined");
crm_config_err("Either configure some or disable STONITH with the stonith-enabled option");
crm_config_err("NOTE: Clusters with shared data need STONITH to ensure data integrity");
}
return TRUE;
}
+/* The ticket state section:
+ * "/cib/status/tickets/ticket_state" */
+static gboolean
+unpack_ticket_state(xmlNode * xml_ticket, pe_working_set_t * data_set)
+{
+ const char *ticket_id = NULL;
+ const char *granted = NULL;
+ const char *last_granted = NULL;
+ const char *standby = NULL;
+ xmlAttrPtr xIter = NULL;
+
+ ticket_t *ticket = NULL;
+
+ ticket_id = ID(xml_ticket);
+ if (ticket_id == NULL || strlen(ticket_id) == 0) {
+ return FALSE;
+ }
+
+ crm_trace("Processing ticket state for %s", ticket_id);
+
+ ticket = g_hash_table_lookup(data_set->tickets, ticket_id);
+ if (ticket == NULL) {
+ ticket = ticket_new(ticket_id, data_set);
+ if (ticket == NULL) {
+ return FALSE;
+ }
+ }
+
+ for (xIter = xml_ticket->properties; xIter; xIter = xIter->next) {
+ const char *prop_name = (const char *)xIter->name;
+ const char *prop_value = crm_element_value(xml_ticket, prop_name);
+
+ if(crm_str_eq(prop_name, XML_ATTR_ID, TRUE)) {
+ continue;
+ }
+ g_hash_table_replace(ticket->state, crm_strdup(prop_name), crm_strdup(prop_value));
+ }
+
+ granted = g_hash_table_lookup(ticket->state, "granted");
+ if (granted && crm_is_true(granted)) {
+ ticket->granted = TRUE;
+ crm_info("We have ticket '%s'", ticket->id);
+ } else {
+ ticket->granted = FALSE;
+ crm_info("We do not have ticket '%s'", ticket->id);
+ }
+
+ last_granted = g_hash_table_lookup(ticket->state, "last-granted");
+ if (last_granted) {
+ ticket->last_granted = crm_parse_int(last_granted, 0);
+ }
+
+ standby = g_hash_table_lookup(ticket->state, "standby");
+ if (standby && crm_is_true(standby)) {
+ ticket->standby = TRUE;
+ if (ticket->granted) {
+ crm_info("Granted ticket '%s' is in standby-mode", ticket->id);
+ }
+ } else {
+ ticket->standby = FALSE;
+ }
+
+ crm_trace("Done with ticket state for %s", ticket_id);
+
+ return TRUE;
+}
+
+static gboolean
+unpack_tickets_state(xmlNode * xml_tickets, pe_working_set_t * data_set)
+{
+ xmlNode *xml_obj = NULL;
+
+ for (xml_obj = __xml_first_child(xml_tickets); xml_obj != NULL; xml_obj = __xml_next(xml_obj)) {
+ if (crm_str_eq((const char *)xml_obj->name, XML_CIB_TAG_TICKET_STATE, TRUE) == FALSE) {
+ continue;
+ }
+ unpack_ticket_state(xml_obj, data_set);
+ }
+
+ return TRUE;
+}
+
+/* Compatibility with the deprecated ticket state section:
+ * "/cib/status/tickets/instance_attributes" */
static void
-get_ticket_state(gpointer key, gpointer value, gpointer user_data)
+get_ticket_state_legacy(gpointer key, gpointer value, gpointer user_data)
{
- const char *attr_key = key;
+ const char *long_key = key;
+ char *state_key = NULL;
const char *granted_prefix = "granted-ticket-";
const char *last_granted_prefix = "last-granted-";
static int granted_prefix_strlen = 0;
static int last_granted_prefix_strlen = 0;
const char *ticket_id = NULL;
const char *is_granted = NULL;
const char *last_granted = NULL;
+ const char *sep = NULL;
ticket_t *ticket = NULL;
- GHashTable *tickets = user_data;
+ pe_working_set_t *data_set = user_data;
if (granted_prefix_strlen == 0) {
granted_prefix_strlen = strlen(granted_prefix);
}
if (last_granted_prefix_strlen == 0) {
last_granted_prefix_strlen = strlen(last_granted_prefix);
}
- if (strstr(attr_key, granted_prefix) == attr_key) {
- ticket_id = attr_key + granted_prefix_strlen;
+ if (strstr(long_key, granted_prefix) == long_key) {
+ ticket_id = long_key + granted_prefix_strlen;
if (strlen(ticket_id)) {
+ state_key = crm_strdup("granted");
is_granted = value;
}
- } else if (strstr(attr_key, last_granted_prefix) == attr_key) {
- ticket_id = attr_key + last_granted_prefix_strlen;
+ } else if (strstr(long_key, last_granted_prefix) == long_key) {
+ ticket_id = long_key + last_granted_prefix_strlen;
if (strlen(ticket_id)) {
+ state_key = crm_strdup("last-granted");
last_granted = value;
}
+ } else if ((sep = strrchr(long_key, '-'))) {
+ ticket_id = sep + 1;
+ state_key = strndup(long_key, strlen(long_key) - strlen(sep));
}
if (ticket_id == NULL || strlen(ticket_id) == 0) {
return;
}
- ticket = g_hash_table_lookup(tickets, ticket_id);
+ if (state_key == NULL || strlen(state_key) == 0) {
+ return;
+ }
+
+ ticket = g_hash_table_lookup(data_set->tickets, ticket_id);
if (ticket == NULL) {
- crm_malloc0(ticket, sizeof(ticket_t));
+ ticket = ticket_new(ticket_id, data_set);
if (ticket == NULL) {
- crm_config_err("Cannot allocate ticket '%s'", ticket_id);
return;
}
-
- ticket->id = crm_strdup(ticket_id);
- ticket->granted = FALSE;
- ticket->last_granted = -1;
-
- g_hash_table_insert(tickets, crm_strdup(ticket->id), ticket);
}
+ g_hash_table_replace(ticket->state, state_key, crm_strdup(value));
+
if (is_granted) {
if (crm_is_true(is_granted)) {
ticket->granted = TRUE;
crm_info("We have ticket '%s'", ticket->id);
} else {
ticket->granted = FALSE;
crm_info("We do not have ticket '%s'", ticket->id);
}
} else if (last_granted) {
ticket->last_granted = crm_parse_int(last_granted, 0);
}
}
-static void
-destroy_ticket(gpointer data)
-{
- ticket_t *ticket = data;
-
- crm_free(ticket->id);
- crm_free(ticket);
-}
-
/* remove nodes that are down, stopping */
/* create +ve rsc_to_node constraints between resources and the nodes they are running on */
/* anything else? */
gboolean
unpack_status(xmlNode * status, pe_working_set_t * data_set)
{
const char *id = NULL;
const char *uname = NULL;
xmlNode *lrm_rsc = NULL;
xmlNode *attrs = NULL;
xmlNode *state = NULL;
xmlNode *node_state = NULL;
node_t *this_node = NULL;
crm_trace("Beginning unpack");
- data_set->tickets =
- g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, destroy_ticket);
+ if (data_set->tickets == NULL) {
+ data_set->tickets =
+ g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, destroy_ticket);
+ }
for (state = __xml_first_child(status); state != NULL; state = __xml_next(state)) {
if (crm_str_eq((const char *)state->name, XML_CIB_TAG_TICKETS, TRUE)) {
- xmlNode *tickets = state;
- GHashTable *attrs_hash =
- g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str,
+ xmlNode *xml_tickets = state;
+ GHashTable *state_hash = NULL;
+
+ /* Compatibility with the deprecated ticket state section:
+ * Unpack the attributes in the deprecated "/cib/status/tickets/instance_attributes" if it exists. */
+ state_hash = g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str,
g_hash_destroy_str);
- unpack_instance_attributes(data_set->input, tickets, XML_TAG_ATTR_SETS, NULL,
- attrs_hash, NULL, TRUE, data_set->now);
+ unpack_instance_attributes(data_set->input, xml_tickets, XML_TAG_ATTR_SETS, NULL,
+ state_hash, NULL, TRUE, data_set->now);
- g_hash_table_foreach(attrs_hash, get_ticket_state, data_set->tickets);
+ g_hash_table_foreach(state_hash, get_ticket_state_legacy, data_set);
- if (attrs_hash) {
- g_hash_table_destroy(attrs_hash);
+ if (state_hash) {
+ g_hash_table_destroy(state_hash);
}
+
+ /* Unpack the new "/cib/status/tickets/ticket_state"s */
+ unpack_tickets_state(xml_tickets, data_set);
}
if (crm_str_eq((const char *)state->name, XML_CIB_TAG_STATE, TRUE)) {
node_state = state;
id = crm_element_value(node_state, XML_ATTR_ID);
uname = crm_element_value(node_state, XML_ATTR_UNAME);
attrs = find_xml_node(node_state, XML_TAG_TRANSIENT_NODEATTRS, FALSE);
crm_trace("Processing node id=%s, uname=%s", id, uname);
this_node = pe_find_node_id(data_set->nodes, id);
if (uname == NULL) {
/* error */
continue;
} else if (this_node == NULL) {
crm_config_warn("Node %s in status section no longer exists", uname);
continue;
}
/* Mark the node as provisionally clean
* - at least we have seen it in the current cluster's lifetime
*/
this_node->details->unclean = FALSE;
add_node_attrs(attrs, this_node, TRUE, data_set);
if (crm_is_true(g_hash_table_lookup(this_node->details->attrs, "standby"))) {
crm_info("Node %s is in standby-mode", this_node->details->uname);
this_node->details->standby = TRUE;
}
crm_trace("determining node state");
determine_online_status(node_state, this_node, data_set);
if (this_node->details->online && data_set->no_quorum_policy == no_quorum_suicide) {
/* Everything else should flow from this automatically
* At least until the PE becomes able to migrate off healthy resources
*/
pe_fence_node(data_set, this_node, "because the cluster does not have quorum");
}
}
}
/* Now that we know all node states, we can safely handle migration ops
* But, for now, only process healthy nodes
* - this is necessary for the logic in bug lf#2508 to function correctly
*/
for (node_state = __xml_first_child(status); node_state != NULL;
node_state = __xml_next(node_state)) {
if (crm_str_eq((const char *)node_state->name, XML_CIB_TAG_STATE, TRUE) == FALSE) {
continue;
}
id = crm_element_value(node_state, XML_ATTR_ID);
this_node = pe_find_node_id(data_set->nodes, id);
if (this_node == NULL) {
crm_info("Node %s is unknown", id);
continue;
} else if (this_node->details->online) {
crm_trace("Processing lrm resource entries on healthy node: %s",
this_node->details->uname);
lrm_rsc = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE);
lrm_rsc = find_xml_node(lrm_rsc, XML_LRM_TAG_RESOURCES, FALSE);
unpack_lrm_resources(this_node, lrm_rsc, data_set);
}
}
/* Now handle failed nodes - but only if stonith is enabled
*
* By definition, offline nodes run no resources so there is nothing to do.
* Only when stonith is enabled do we need to know what is on the node to
* ensure rsc start events happen after the stonith
*/
for (node_state = __xml_first_child(status);
node_state != NULL && is_set(data_set->flags, pe_flag_stonith_enabled);
node_state = __xml_next(node_state)) {
if (crm_str_eq((const char *)node_state->name, XML_CIB_TAG_STATE, TRUE) == FALSE) {
continue;
}
id = crm_element_value(node_state, XML_ATTR_ID);
this_node = pe_find_node_id(data_set->nodes, id);
if (this_node == NULL || this_node->details->online) {
continue;
} else {
crm_trace("Processing lrm resource entries on unhealthy node: %s",
this_node->details->uname);
lrm_rsc = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE);
lrm_rsc = find_xml_node(lrm_rsc, XML_LRM_TAG_RESOURCES, FALSE);
unpack_lrm_resources(this_node, lrm_rsc, data_set);
}
}
return TRUE;
}
static gboolean
determine_online_status_no_fencing(pe_working_set_t * data_set, xmlNode * node_state,
node_t * this_node)
{
gboolean online = FALSE;
const char *join_state = crm_element_value(node_state, XML_CIB_ATTR_JOINSTATE);
const char *crm_state = crm_element_value(node_state, XML_CIB_ATTR_CRMDSTATE);
const char *ccm_state = crm_element_value(node_state, XML_CIB_ATTR_INCCM);
const char *ha_state = crm_element_value(node_state, XML_CIB_ATTR_HASTATE);
const char *exp_state = crm_element_value(node_state, XML_CIB_ATTR_EXPSTATE);
if (ha_state == NULL) {
ha_state = DEADSTATUS;
}
if (!crm_is_true(ccm_state) || safe_str_eq(ha_state, DEADSTATUS)) {
crm_trace("Node is down: ha_state=%s, ccm_state=%s",
crm_str(ha_state), crm_str(ccm_state));
} else if (safe_str_eq(crm_state, ONLINESTATUS)) {
if (safe_str_eq(join_state, CRMD_JOINSTATE_MEMBER)) {
online = TRUE;
} else {
crm_debug("Node is not ready to run resources: %s", join_state);
}
} else if (this_node->details->expected_up == FALSE) {
crm_trace("CRMd is down: ha_state=%s, ccm_state=%s",
crm_str(ha_state), crm_str(ccm_state));
crm_trace("\tcrm_state=%s, join_state=%s, expected=%s",
crm_str(crm_state), crm_str(join_state), crm_str(exp_state));
} else {
/* mark it unclean */
pe_fence_node(data_set, this_node, "because it is partially and/or un-expectedly down");
crm_info("\tha_state=%s, ccm_state=%s,"
" crm_state=%s, join_state=%s, expected=%s",
crm_str(ha_state), crm_str(ccm_state),
crm_str(crm_state), crm_str(join_state), crm_str(exp_state));
}
return online;
}
static gboolean
determine_online_status_fencing(pe_working_set_t * data_set, xmlNode * node_state,
node_t * this_node)
{
gboolean online = FALSE;
gboolean do_terminate = FALSE;
const char *join_state = crm_element_value(node_state, XML_CIB_ATTR_JOINSTATE);
const char *crm_state = crm_element_value(node_state, XML_CIB_ATTR_CRMDSTATE);
const char *ccm_state = crm_element_value(node_state, XML_CIB_ATTR_INCCM);
const char *ha_state = crm_element_value(node_state, XML_CIB_ATTR_HASTATE);
const char *exp_state = crm_element_value(node_state, XML_CIB_ATTR_EXPSTATE);
const char *terminate = g_hash_table_lookup(this_node->details->attrs, "terminate");
if (ha_state == NULL) {
ha_state = DEADSTATUS;
}
if (crm_is_true(terminate)) {
do_terminate = TRUE;
} else if (terminate != NULL && strlen(terminate) > 0) {
/* could be a time() value */
char t = terminate[0];
if (t != '0' && isdigit(t)) {
do_terminate = TRUE;
}
}
if (crm_is_true(ccm_state)
&& safe_str_eq(ha_state, ACTIVESTATUS)
&& safe_str_eq(crm_state, ONLINESTATUS)) {
if (safe_str_eq(join_state, CRMD_JOINSTATE_MEMBER)) {
online = TRUE;
if (do_terminate) {
pe_fence_node(data_set, this_node, "because termination was requested");
}
} else if (join_state == exp_state /* == NULL */ ) {
crm_info("Node %s is coming up", this_node->details->uname);
crm_debug("\tha_state=%s, ccm_state=%s,"
" crm_state=%s, join_state=%s, expected=%s",
crm_str(ha_state), crm_str(ccm_state),
crm_str(crm_state), crm_str(join_state), crm_str(exp_state));
} else if (safe_str_eq(join_state, CRMD_JOINSTATE_PENDING)) {
crm_info("Node %s is not ready to run resources", this_node->details->uname);
this_node->details->standby = TRUE;
this_node->details->pending = TRUE;
online = TRUE;
} else if (safe_str_eq(join_state, CRMD_JOINSTATE_NACK)) {
crm_warn("Node %s is not part of the cluster", this_node->details->uname);
this_node->details->standby = TRUE;
this_node->details->pending = TRUE;
online = TRUE;
} else if (safe_str_eq(join_state, exp_state)) {
crm_info("Node %s is still coming up: %s", this_node->details->uname, join_state);
crm_info("\tha_state=%s, ccm_state=%s, crm_state=%s",
crm_str(ha_state), crm_str(ccm_state), crm_str(crm_state));
this_node->details->standby = TRUE;
this_node->details->pending = TRUE;
online = TRUE;
} else {
crm_warn("Node %s (%s) is un-expectedly down",
this_node->details->uname, this_node->details->id);
crm_info("\tha_state=%s, ccm_state=%s,"
" crm_state=%s, join_state=%s, expected=%s",
crm_str(ha_state), crm_str(ccm_state),
crm_str(crm_state), crm_str(join_state), crm_str(exp_state));
pe_fence_node(data_set, this_node, "because it is un-expectedly down");
}
} else if (crm_is_true(ccm_state) == FALSE && safe_str_eq(ha_state, DEADSTATUS)
&& safe_str_eq(crm_state, OFFLINESTATUS)
&& this_node->details->expected_up == FALSE) {
crm_debug("Node %s is down: join_state=%s, expected=%s",
this_node->details->uname, crm_str(join_state), crm_str(exp_state));
#if 0
/* While a nice optimization, it causes the cluster to block until the node
* comes back online. Which is a serious problem if the cluster software
* is not configured to start at boot or stonith is configured to merely
* stop the node instead of restart it.
* Easily triggered by setting terminate=true for the DC
*/
} else if (do_terminate) {
crm_info("Node %s is %s after forced termination",
this_node->details->uname, crm_is_true(ccm_state) ? "coming up" : "going down");
crm_debug("\tha_state=%s, ccm_state=%s,"
" crm_state=%s, join_state=%s, expected=%s",
crm_str(ha_state), crm_str(ccm_state),
crm_str(crm_state), crm_str(join_state), crm_str(exp_state));
if (crm_is_true(ccm_state) == FALSE) {
this_node->details->standby = TRUE;
this_node->details->pending = TRUE;
online = TRUE;
}
#endif
} else if (this_node->details->expected_up) {
/* mark it unclean */
pe_fence_node(data_set, this_node, "because it is un-expectedly down");
crm_info("\tha_state=%s, ccm_state=%s,"
" crm_state=%s, join_state=%s, expected=%s",
crm_str(ha_state), crm_str(ccm_state),
crm_str(crm_state), crm_str(join_state), crm_str(exp_state));
} else {
crm_info("Node %s is down", this_node->details->uname);
crm_debug("\tha_state=%s, ccm_state=%s,"
" crm_state=%s, join_state=%s, expected=%s",
crm_str(ha_state), crm_str(ccm_state),
crm_str(crm_state), crm_str(join_state), crm_str(exp_state));
}
return online;
}
gboolean
determine_online_status(xmlNode * node_state, node_t * this_node, pe_working_set_t * data_set)
{
gboolean online = FALSE;
const char *shutdown = NULL;
const char *exp_state = crm_element_value(node_state, XML_CIB_ATTR_EXPSTATE);
if (this_node == NULL) {
crm_config_err("No node to check");
return online;
}
this_node->details->shutdown = FALSE;
this_node->details->expected_up = FALSE;
shutdown = g_hash_table_lookup(this_node->details->attrs, XML_CIB_ATTR_SHUTDOWN);
if (shutdown != NULL && safe_str_neq("0", shutdown)) {
this_node->details->shutdown = TRUE;
} else if (safe_str_eq(exp_state, CRMD_JOINSTATE_MEMBER)) {
this_node->details->expected_up = TRUE;
}
if (is_set(data_set->flags, pe_flag_stonith_enabled) == FALSE) {
online = determine_online_status_no_fencing(data_set, node_state, this_node);
} else {
online = determine_online_status_fencing(data_set, node_state, this_node);
}
if (online) {
this_node->details->online = TRUE;
} else {
/* remove node from contention */
this_node->fixed = TRUE;
this_node->weight = -INFINITY;
}
if (online && this_node->details->shutdown) {
/* dont run resources here */
this_node->fixed = TRUE;
this_node->weight = -INFINITY;
}
if (this_node->details->unclean) {
pe_proc_warn("Node %s is unclean", this_node->details->uname);
} else if (this_node->details->online) {
crm_info("Node %s is %s", this_node->details->uname,
this_node->details->shutdown ? "shutting down" :
this_node->details->pending ? "pending" :
this_node->details->standby ? "standby" : "online");
} else {
crm_trace("Node %s is offline", this_node->details->uname);
}
return online;
}
#define set_char(x) last_rsc_id[lpc] = x; complete = TRUE;
char *
clone_zero(const char *last_rsc_id)
{
int lpc = 0;
char *zero = NULL;
CRM_CHECK(last_rsc_id != NULL, return NULL);
if (last_rsc_id != NULL) {
lpc = strlen(last_rsc_id);
}
while (--lpc > 0) {
switch (last_rsc_id[lpc]) {
case 0:
return NULL;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
break;
case ':':
crm_malloc0(zero, lpc + 3);
memcpy(zero, last_rsc_id, lpc);
zero[lpc] = ':';
zero[lpc + 1] = '0';
zero[lpc + 2] = 0;
return zero;
}
}
return NULL;
}
char *
increment_clone(char *last_rsc_id)
{
int lpc = 0;
int len = 0;
char *tmp = NULL;
gboolean complete = FALSE;
CRM_CHECK(last_rsc_id != NULL, return NULL);
if (last_rsc_id != NULL) {
len = strlen(last_rsc_id);
}
lpc = len - 1;
while (complete == FALSE && lpc > 0) {
switch (last_rsc_id[lpc]) {
case 0:
lpc--;
break;
case '0':
set_char('1');
break;
case '1':
set_char('2');
break;
case '2':
set_char('3');
break;
case '3':
set_char('4');
break;
case '4':
set_char('5');
break;
case '5':
set_char('6');
break;
case '6':
set_char('7');
break;
case '7':
set_char('8');
break;
case '8':
set_char('9');
break;
case '9':
last_rsc_id[lpc] = '0';
lpc--;
break;
case ':':
tmp = last_rsc_id;
crm_malloc0(last_rsc_id, len + 2);
memcpy(last_rsc_id, tmp, len);
last_rsc_id[++lpc] = '1';
last_rsc_id[len] = '0';
last_rsc_id[len + 1] = 0;
complete = TRUE;
crm_free(tmp);
break;
default:
crm_err("Unexpected char: %c (%d)", last_rsc_id[lpc], lpc);
return NULL;
break;
}
}
return last_rsc_id;
}
static int
get_clone(char *last_rsc_id)
{
int clone = 0;
int lpc = 0;
int len = 0;
CRM_CHECK(last_rsc_id != NULL, return -1);
if (last_rsc_id != NULL) {
len = strlen(last_rsc_id);
}
lpc = len - 1;
while (lpc > 0) {
switch (last_rsc_id[lpc]) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
clone += (int)(last_rsc_id[lpc] - '0') * (len - lpc);
lpc--;
break;
case ':':
return clone;
break;
default:
crm_err("Unexpected char: %d (%c)", lpc, last_rsc_id[lpc]);
return clone;
break;
}
}
return -1;
}
static resource_t *
create_fake_resource(const char *rsc_id, xmlNode * rsc_entry, pe_working_set_t * data_set)
{
resource_t *rsc = NULL;
xmlNode *xml_rsc = create_xml_node(NULL, XML_CIB_TAG_RESOURCE);
copy_in_properties(xml_rsc, rsc_entry);
crm_xml_add(xml_rsc, XML_ATTR_ID, rsc_id);
crm_log_xml_debug(xml_rsc, "Orphan resource");
if (!common_unpack(xml_rsc, &rsc, NULL, data_set)) {
return NULL;
}
set_bit(rsc->flags, pe_rsc_orphan);
data_set->resources = g_list_append(data_set->resources, rsc);
return rsc;
}
extern resource_t *create_child_clone(resource_t * rsc, int sub_id, pe_working_set_t * data_set);
static resource_t *
find_clone(pe_working_set_t * data_set, node_t * node, resource_t * parent, const char *rsc_id)
{
int len = 0;
resource_t *rsc = NULL;
char *base = clone_zero(rsc_id);
char *alt_rsc_id = NULL;
CRM_ASSERT(parent != NULL);
CRM_ASSERT(parent->variant == pe_clone || parent->variant == pe_master);
if (base) {
len = strlen(base);
}
if (len > 0) {
base[len - 1] = 0;
}
crm_trace("Looking for %s on %s in %s %d",
rsc_id, node->details->uname, parent->id, is_set(parent->flags, pe_rsc_unique));
if (is_set(parent->flags, pe_rsc_unique)) {
crm_trace("Looking for %s", rsc_id);
rsc = parent->fns->find_rsc(parent, rsc_id, NULL, pe_find_current);
} else {
crm_trace("Looking for %s on %s", base, node->details->uname);
rsc = parent->fns->find_rsc(parent, base, node, pe_find_partial | pe_find_current);
if (rsc != NULL && rsc->running_on) {
GListPtr gIter = parent->children;
rsc = NULL;
crm_trace("Looking for an existing orphan for %s: %s on %s", parent->id, rsc_id,
node->details->uname);
/* There is already an instance of this _anonymous_ clone active on "node".
*
* If there is a partially active orphan (only applies to clone groups) on
* the same node, use that.
* Otherwise create a new (orphaned) instance at "orphan_check:".
*/
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child = (resource_t *) gIter->data;
node_t *loc = child->fns->location(child, NULL, TRUE);
if (loc && loc->details == node->details) {
resource_t *tmp =
child->fns->find_rsc(child, base, NULL, pe_find_partial | pe_find_current);
if (tmp && tmp->running_on == NULL) {
rsc = tmp;
break;
}
}
}
goto orphan_check;
} else if (((resource_t *) parent->children->data)->variant == pe_group) {
/* If we're grouped, we need to look for a peer thats active on $node
* and use their clone instance number
*/
resource_t *peer =
parent->fns->find_rsc(parent, NULL, node, pe_find_clone | pe_find_current);
if (peer && peer->running_on) {
char buffer[256];
int clone_num = get_clone(peer->id);
snprintf(buffer, 256, "%s%d", base, clone_num);
rsc =
parent->fns->find_rsc(parent, buffer, node, pe_find_current | pe_find_inactive);
if (rsc) {
crm_trace("Found someone active: %s on %s, becoming %s", peer->id,
((node_t *) peer->running_on->data)->details->uname, buffer);
}
}
}
if(parent->fns->find_rsc(parent, rsc_id, NULL, pe_find_current)) {
alt_rsc_id = crm_strdup(rsc_id);
} else {
alt_rsc_id = clone_zero(rsc_id);
}
while (rsc == NULL) {
rsc = parent->fns->find_rsc(parent, alt_rsc_id, NULL, pe_find_current);
if (rsc == NULL) {
crm_trace("Unknown resource: %s", alt_rsc_id);
break;
}
if (rsc->running_on == NULL) {
crm_trace("Resource %s: just right", alt_rsc_id);
break;
}
crm_trace("Resource %s: already active", alt_rsc_id);
alt_rsc_id = increment_clone(alt_rsc_id);
rsc = NULL;
}
}
orphan_check:
if (rsc == NULL) {
/* Create an extra orphan */
resource_t *top = create_child_clone(parent, -1, data_set);
crm_debug("Created orphan for %s: %s on %s", parent->id, rsc_id, node->details->uname);
rsc = top->fns->find_rsc(top, base, NULL, pe_find_current | pe_find_partial);
CRM_ASSERT(rsc != NULL);
}
crm_free(rsc->clone_name);
rsc->clone_name = NULL;
if (safe_str_neq(rsc_id, rsc->id)) {
crm_info("Internally renamed %s on %s to %s%s",
rsc_id, node->details->uname, rsc->id,
is_set(rsc->flags, pe_rsc_orphan) ? " (ORPHAN)" : "");
rsc->clone_name = crm_strdup(rsc_id);
}
crm_free(alt_rsc_id);
crm_free(base);
return rsc;
}
static resource_t *
unpack_find_resource(pe_working_set_t * data_set, node_t * node, const char *rsc_id,
xmlNode * rsc_entry)
{
resource_t *rsc = NULL;
resource_t *clone_parent = NULL;
char *alt_rsc_id = crm_strdup(rsc_id);
crm_trace("looking for %s", rsc_id);
rsc = pe_find_resource(data_set->resources, alt_rsc_id);
/* no match */
if (rsc == NULL) {
/* Even when clone-max=0, we still create a single :0 orphan to match against */
char *tmp = clone_zero(alt_rsc_id);
resource_t *clone0 = pe_find_resource(data_set->resources, tmp);
clone_parent = uber_parent(clone0);
crm_free(tmp);
crm_trace("%s not found: %s", alt_rsc_id, clone_parent ? clone_parent->id : "orphan");
} else {
clone_parent = uber_parent(rsc);
}
if (clone_parent && clone_parent->variant > pe_group) {
rsc = find_clone(data_set, node, clone_parent, rsc_id);
CRM_ASSERT(rsc != NULL);
}
crm_free(alt_rsc_id);
return rsc;
}
static resource_t *
process_orphan_resource(xmlNode * rsc_entry, node_t * node, pe_working_set_t * data_set)
{
resource_t *rsc = NULL;
const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID);
crm_debug("Detected orphan resource %s on %s", rsc_id, node->details->uname);
rsc = create_fake_resource(rsc_id, rsc_entry, data_set);
if (is_set(data_set->flags, pe_flag_stop_rsc_orphans) == FALSE) {
clear_bit(rsc->flags, pe_rsc_managed);
} else {
GListPtr gIter = NULL;
print_resource(LOG_DEBUG_3, "Added orphan", rsc, FALSE);
CRM_CHECK(rsc != NULL, return NULL);
resource_location(rsc, NULL, -INFINITY, "__orphan_dont_run__", data_set);
for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
node_t *node = (node_t *) gIter->data;
if(node->details->online && get_failcount(node, rsc, NULL, data_set)) {
action_t *clear_op = NULL;
action_t *ready = get_pseudo_op(CRM_OP_PROBED, data_set);
clear_op = custom_action(rsc, crm_concat(rsc->id, CRM_OP_CLEAR_FAILCOUNT, '_'),
CRM_OP_CLEAR_FAILCOUNT, node, FALSE, TRUE, data_set);
add_hash_param(clear_op->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE);
crm_info("Clearing failcount (%d) for orphaned resource %s on %s (%s)",
get_failcount(node, rsc, NULL, data_set), rsc->id, node->details->uname, clear_op->uuid);
order_actions(clear_op, ready, pe_order_optional);
}
}
}
return rsc;
}
static void
process_rsc_state(resource_t * rsc, node_t * node,
enum action_fail_response on_fail,
xmlNode * migrate_op, pe_working_set_t * data_set)
{
crm_trace("Resource %s is %s on %s: on_fail=%s",
rsc->id, role2text(rsc->role), node->details->uname, fail2text(on_fail));
/* process current state */
if (rsc->role != RSC_ROLE_UNKNOWN) {
resource_t *iter = rsc;
while (iter) {
if (g_hash_table_lookup(iter->known_on, node->details->id) == NULL) {
node_t *n = node_copy(node);
g_hash_table_insert(iter->known_on, (gpointer) n->details->id, n);
}
if (is_set(iter->flags, pe_rsc_unique)) {
break;
}
iter = iter->parent;
}
}
if (node->details->unclean) {
/* No extra processing needed
* Also allows resources to be started again after a node is shot
*/
on_fail = action_fail_ignore;
}
switch (on_fail) {
case action_fail_ignore:
/* nothing to do */
break;
case action_fail_fence:
/* treat it as if it is still running
* but also mark the node as unclean
*/
pe_fence_node(data_set, node, "to recover from resource failure(s)");
break;
case action_fail_standby:
node->details->standby = TRUE;
node->details->standby_onfail = TRUE;
break;
case action_fail_block:
/* is_managed == FALSE will prevent any
* actions being sent for the resource
*/
clear_bit(rsc->flags, pe_rsc_managed);
set_bit(rsc->flags, pe_rsc_block);
break;
case action_fail_migrate:
/* make sure it comes up somewhere else
* or not at all
*/
resource_location(rsc, node, -INFINITY, "__action_migration_auto__", data_set);
break;
case action_fail_stop:
rsc->next_role = RSC_ROLE_STOPPED;
break;
case action_fail_recover:
if (rsc->role != RSC_ROLE_STOPPED && rsc->role != RSC_ROLE_UNKNOWN) {
set_bit(rsc->flags, pe_rsc_failed);
stop_action(rsc, node, FALSE);
}
break;
}
if (rsc->role != RSC_ROLE_STOPPED && rsc->role != RSC_ROLE_UNKNOWN) {
if (is_set(rsc->flags, pe_rsc_orphan)) {
if (is_set(rsc->flags, pe_rsc_managed)) {
crm_config_warn("Detected active orphan %s running on %s",
rsc->id, node->details->uname);
} else {
crm_config_warn("Cluster configured not to stop active orphans."
" %s must be stopped manually on %s",
rsc->id, node->details->uname);
}
}
native_add_running(rsc, node, data_set);
if (on_fail != action_fail_ignore) {
set_bit(rsc->flags, pe_rsc_failed);
}
} else if (rsc->clone_name) {
crm_trace("Resetting clone_name %s for %s (stopped)", rsc->clone_name, rsc->id);
crm_free(rsc->clone_name);
rsc->clone_name = NULL;
} else {
char *key = stop_key(rsc);
GListPtr possible_matches = find_actions(rsc->actions, key, node);
GListPtr gIter = possible_matches;
for (; gIter != NULL; gIter = gIter->next) {
action_t *stop = (action_t *) gIter->data;
stop->flags |= pe_action_optional;
}
crm_free(key);
}
}
/* create active recurring operations as optional */
static void
process_recurring(node_t * node, resource_t * rsc,
int start_index, int stop_index,
GListPtr sorted_op_list, pe_working_set_t * data_set)
{
int counter = -1;
const char *task = NULL;
const char *status = NULL;
GListPtr gIter = sorted_op_list;
crm_trace("%s: Start index %d, stop index = %d", rsc->id, start_index, stop_index);
for (; gIter != NULL; gIter = gIter->next) {
xmlNode *rsc_op = (xmlNode *) gIter->data;
int interval = 0;
char *key = NULL;
const char *id = ID(rsc_op);
const char *interval_s = NULL;
counter++;
if (node->details->online == FALSE) {
crm_trace("Skipping %s/%s: node is offline", rsc->id, node->details->uname);
break;
/* Need to check if there's a monitor for role="Stopped" */
} else if (start_index < stop_index && counter <= stop_index) {
crm_trace("Skipping %s/%s: resource is not active", id, node->details->uname);
continue;
} else if (counter < start_index) {
crm_trace("Skipping %s/%s: old %d", id, node->details->uname, counter);
continue;
}
interval_s = crm_element_value(rsc_op, XML_LRM_ATTR_INTERVAL);
interval = crm_parse_int(interval_s, "0");
if (interval == 0) {
crm_trace("Skipping %s/%s: non-recurring", id, node->details->uname);
continue;
}
status = crm_element_value(rsc_op, XML_LRM_ATTR_OPSTATUS);
if (safe_str_eq(status, "-1")) {
crm_trace("Skipping %s/%s: status", id, node->details->uname);
continue;
}
task = crm_element_value(rsc_op, XML_LRM_ATTR_TASK);
/* create the action */
key = generate_op_key(rsc->id, task, interval);
crm_trace("Creating %s/%s", key, node->details->uname);
custom_action(rsc, key, task, node, TRUE, TRUE, data_set);
}
}
void
calculate_active_ops(GListPtr sorted_op_list, int *start_index, int *stop_index)
{
int counter = -1;
const char *task = NULL;
const char *status = NULL;
GListPtr gIter = sorted_op_list;
*stop_index = -1;
*start_index = -1;
for (; gIter != NULL; gIter = gIter->next) {
xmlNode *rsc_op = (xmlNode *) gIter->data;
counter++;
task = crm_element_value(rsc_op, XML_LRM_ATTR_TASK);
status = crm_element_value(rsc_op, XML_LRM_ATTR_OPSTATUS);
if (safe_str_eq(task, CRMD_ACTION_STOP)
&& safe_str_eq(status, "0")) {
*stop_index = counter;
} else if (safe_str_eq(task, CRMD_ACTION_START) || safe_str_eq(task, CRMD_ACTION_MIGRATED)) {
*start_index = counter;
} else if (*start_index <= *stop_index && safe_str_eq(task, CRMD_ACTION_STATUS)) {
const char *rc = crm_element_value(rsc_op, XML_LRM_ATTR_RC);
if (safe_str_eq(rc, "0") || safe_str_eq(rc, "8")) {
*start_index = counter;
}
}
}
}
static void
unpack_lrm_rsc_state(node_t * node, xmlNode * rsc_entry, pe_working_set_t * data_set)
{
GListPtr gIter = NULL;
int stop_index = -1;
int start_index = -1;
enum rsc_role_e req_role = RSC_ROLE_UNKNOWN;
const char *task = NULL;
const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID);
resource_t *rsc = NULL;
GListPtr op_list = NULL;
GListPtr sorted_op_list = NULL;
xmlNode *migrate_op = NULL;
xmlNode *rsc_op = NULL;
enum action_fail_response on_fail = FALSE;
enum rsc_role_e saved_role = RSC_ROLE_UNKNOWN;
crm_trace("[%s] Processing %s on %s",
crm_element_name(rsc_entry), rsc_id, node->details->uname);
/* extract operations */
op_list = NULL;
sorted_op_list = NULL;
for (rsc_op = __xml_first_child(rsc_entry); rsc_op != NULL; rsc_op = __xml_next(rsc_op)) {
if (crm_str_eq((const char *)rsc_op->name, XML_LRM_TAG_RSC_OP, TRUE)) {
op_list = g_list_prepend(op_list, rsc_op);
}
}
if (op_list == NULL) {
/* if there are no operations, there is nothing to do */
return;
}
/* find the resource */
rsc = unpack_find_resource(data_set, node, rsc_id, rsc_entry);
if (rsc == NULL) {
rsc = process_orphan_resource(rsc_entry, node, data_set);
}
CRM_ASSERT(rsc != NULL);
/* process operations */
saved_role = rsc->role;
on_fail = action_fail_ignore;
rsc->role = RSC_ROLE_UNKNOWN;
sorted_op_list = g_list_sort(op_list, sort_op_by_callid);
for (gIter = sorted_op_list; gIter != NULL; gIter = gIter->next) {
xmlNode *rsc_op = (xmlNode *) gIter->data;
task = crm_element_value(rsc_op, XML_LRM_ATTR_TASK);
if (safe_str_eq(task, CRMD_ACTION_MIGRATED)) {
migrate_op = rsc_op;
}
unpack_rsc_op(rsc, node, rsc_op, gIter->next, &on_fail, data_set);
}
/* create active recurring operations as optional */
calculate_active_ops(sorted_op_list, &start_index, &stop_index);
process_recurring(node, rsc, start_index, stop_index, sorted_op_list, data_set);
/* no need to free the contents */
g_list_free(sorted_op_list);
process_rsc_state(rsc, node, on_fail, migrate_op, data_set);
if (get_target_role(rsc, &req_role)) {
if (rsc->next_role == RSC_ROLE_UNKNOWN || req_role < rsc->next_role) {
crm_debug("%s: Overwriting calculated next role %s"
" with requested next role %s",
rsc->id, role2text(rsc->next_role), role2text(req_role));
rsc->next_role = req_role;
} else if (req_role > rsc->next_role) {
crm_info("%s: Not overwriting calculated next role %s"
" with requested next role %s",
rsc->id, role2text(rsc->next_role), role2text(req_role));
}
}
if (saved_role > rsc->role) {
rsc->role = saved_role;
}
}
gboolean
unpack_lrm_resources(node_t * node, xmlNode * lrm_rsc_list, pe_working_set_t * data_set)
{
xmlNode *rsc_entry = NULL;
CRM_CHECK(node != NULL, return FALSE);
crm_trace("Unpacking resources on %s", node->details->uname);
for (rsc_entry = __xml_first_child(lrm_rsc_list); rsc_entry != NULL;
rsc_entry = __xml_next(rsc_entry)) {
if (crm_str_eq((const char *)rsc_entry->name, XML_LRM_TAG_RESOURCE, TRUE)) {
unpack_lrm_rsc_state(node, rsc_entry, data_set);
}
}
return TRUE;
}
static void
set_active(resource_t * rsc)
{
resource_t *top = uber_parent(rsc);
if (top && top->variant == pe_master) {
rsc->role = RSC_ROLE_SLAVE;
} else {
rsc->role = RSC_ROLE_STARTED;
}
}
static void
set_node_score(gpointer key, gpointer value, gpointer user_data)
{
node_t *node = value;
int *score = user_data;
node->weight = *score;
}
#define STATUS_PATH_MAX 1024
static xmlNode *
find_lrm_op(const char *resource, const char *op, const char *node, const char *source,
pe_working_set_t * data_set)
{
int offset = 0;
char xpath[STATUS_PATH_MAX];
offset += snprintf(xpath + offset, STATUS_PATH_MAX - offset, "//node_state[@uname='%s']", node);
offset +=
snprintf(xpath + offset, STATUS_PATH_MAX - offset, "//" XML_LRM_TAG_RESOURCE "[@id='%s']",
resource);
/* Need to check against transition_magic too? */
if (source && safe_str_eq(op, CRMD_ACTION_MIGRATE)) {
offset +=
snprintf(xpath + offset, STATUS_PATH_MAX - offset,
"/" XML_LRM_TAG_RSC_OP "[@operation='%s' and @migrate_target='%s']", op,
source);
} else if (source && safe_str_eq(op, CRMD_ACTION_MIGRATED)) {
offset +=
snprintf(xpath + offset, STATUS_PATH_MAX - offset,
"/" XML_LRM_TAG_RSC_OP "[@operation='%s' and @migrate_source='%s']", op,
source);
} else {
offset +=
snprintf(xpath + offset, STATUS_PATH_MAX - offset,
"/" XML_LRM_TAG_RSC_OP "[@operation='%s']", op);
}
return get_xpath_object(xpath, data_set->input, LOG_DEBUG);
}
gboolean
unpack_rsc_op(resource_t * rsc, node_t * node, xmlNode * xml_op, GListPtr next,
enum action_fail_response * on_fail, pe_working_set_t * data_set)
{
int task_id = 0;
const char *id = NULL;
const char *key = NULL;
const char *task = NULL;
const char *magic = NULL;
const char *actual_rc = NULL;
/* const char *target_rc = NULL; */
const char *task_status = NULL;
const char *interval_s = NULL;
const char *op_version = NULL;
int interval = 0;
int task_status_i = -2;
int actual_rc_i = 0;
int target_rc = -1;
int last_failure = 0;
action_t *action = NULL;
node_t *effective_node = NULL;
resource_t *failed = NULL;
gboolean expired = FALSE;
gboolean is_probe = FALSE;
gboolean clear_past_failure = FALSE;
CRM_CHECK(rsc != NULL, return FALSE);
CRM_CHECK(node != NULL, return FALSE);
CRM_CHECK(xml_op != NULL, return FALSE);
id = ID(xml_op);
task = crm_element_value(xml_op, XML_LRM_ATTR_TASK);
task_status = crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS);
op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION);
magic = crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC);
key = crm_element_value(xml_op, XML_ATTR_TRANSITION_KEY);
crm_element_value_int(xml_op, XML_LRM_ATTR_CALLID, &task_id);
CRM_CHECK(id != NULL, return FALSE);
CRM_CHECK(task != NULL, return FALSE);
CRM_CHECK(task_status != NULL, return FALSE);
task_status_i = crm_parse_int(task_status, NULL);
CRM_CHECK(task_status_i <= LRM_OP_ERROR, return FALSE);
CRM_CHECK(task_status_i >= LRM_OP_PENDING, return FALSE);
if (safe_str_eq(task, CRMD_ACTION_NOTIFY)) {
/* safe to ignore these */
return TRUE;
}
if (rsc->failure_timeout > 0) {
int last_run = 0;
if (crm_element_value_int(xml_op, "last-rc-change", &last_run) == 0) {
time_t now = get_timet_now(data_set);
if (now > (last_run + rsc->failure_timeout)) {
expired = TRUE;
}
}
}
crm_trace("Unpacking task %s/%s (call_id=%d, status=%s) on %s (role=%s)",
id, task, task_id, task_status, node->details->uname, role2text(rsc->role));
interval_s = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL);
interval = crm_parse_int(interval_s, "0");
if (interval == 0 && safe_str_eq(task, CRMD_ACTION_STATUS)) {
is_probe = TRUE;
}
if (node->details->unclean) {
crm_trace("Node %s (where %s is running) is unclean."
" Further action depends on the value of the stop's on-fail attribue",
node->details->uname, rsc->id);
}
actual_rc = crm_element_value(xml_op, XML_LRM_ATTR_RC);
CRM_CHECK(actual_rc != NULL, return FALSE);
actual_rc_i = crm_parse_int(actual_rc, NULL);
if (key) {
int dummy = 0;
char *dummy_string = NULL;
decode_transition_key(key, &dummy_string, &dummy, &dummy, &target_rc);
crm_free(dummy_string);
}
if (task_status_i == LRM_OP_DONE && target_rc >= 0) {
if (target_rc == actual_rc_i) {
task_status_i = LRM_OP_DONE;
} else {
task_status_i = LRM_OP_ERROR;
crm_debug("%s on %s returned %d (%s) instead of the expected value: %d (%s)",
id, node->details->uname,
actual_rc_i, execra_code2string(actual_rc_i),
target_rc, execra_code2string(target_rc));
}
} else if (task_status_i == LRM_OP_ERROR) {
/* let us decide that */
task_status_i = LRM_OP_DONE;
}
if (task_status_i == LRM_OP_NOTSUPPORTED) {
actual_rc_i = EXECRA_UNIMPLEMENT_FEATURE;
}
if (task_status_i != actual_rc_i
&& rsc->failure_timeout > 0 && get_failcount(node, rsc, &last_failure, data_set) == 0) {
if (last_failure > 0) {
action_t *clear_op = NULL;
clear_op = custom_action(rsc, crm_concat(rsc->id, CRM_OP_CLEAR_FAILCOUNT, '_'),
CRM_OP_CLEAR_FAILCOUNT, node, FALSE, TRUE, data_set);
add_hash_param(clear_op->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE);
crm_notice("Clearing expired failcount for %s on %s", rsc->id, node->details->uname);
}
}
if (expired
&& actual_rc_i != EXECRA_NOT_RUNNING
&& actual_rc_i != EXECRA_RUNNING_MASTER && actual_rc_i != EXECRA_OK) {
crm_notice("Ignoring expired failure %s (rc=%d, magic=%s) on %s",
id, actual_rc_i, magic, node->details->uname);
goto done;
}
/* we could clean this up significantly except for old LRMs and CRMs that
* didnt include target_rc and liked to remap status
*/
switch (actual_rc_i) {
case EXECRA_NOT_RUNNING:
if (is_probe || target_rc == actual_rc_i) {
task_status_i = LRM_OP_DONE;
rsc->role = RSC_ROLE_STOPPED;
/* clear any previous failure actions */
*on_fail = action_fail_ignore;
rsc->next_role = RSC_ROLE_UNKNOWN;
} else if (safe_str_neq(task, CRMD_ACTION_STOP)) {
task_status_i = LRM_OP_ERROR;
}
break;
case EXECRA_RUNNING_MASTER:
if (is_probe) {
task_status_i = LRM_OP_DONE;
crm_notice("Operation %s found resource %s active in master mode on %s",
task, rsc->id, node->details->uname);
} else if (target_rc == actual_rc_i) {
/* nothing to do */
} else if (target_rc >= 0) {
task_status_i = LRM_OP_ERROR;
/* legacy code for pre-0.6.5 operations */
} else if (safe_str_neq(task, CRMD_ACTION_STATUS)
|| rsc->role != RSC_ROLE_MASTER) {
task_status_i = LRM_OP_ERROR;
if (rsc->role != RSC_ROLE_MASTER) {
crm_err("%s reported %s in master mode on %s",
id, rsc->id, node->details->uname);
}
}
rsc->role = RSC_ROLE_MASTER;
break;
case EXECRA_FAILED_MASTER:
rsc->role = RSC_ROLE_MASTER;
task_status_i = LRM_OP_ERROR;
break;
case EXECRA_UNIMPLEMENT_FEATURE:
if (interval > 0) {
task_status_i = LRM_OP_NOTSUPPORTED;
break;
}
/* else: fall through */
case EXECRA_INSUFFICIENT_PRIV:
case EXECRA_NOT_INSTALLED:
case EXECRA_INVALID_PARAM:
effective_node = node;
/* fall through */
case EXECRA_NOT_CONFIGURED:
failed = rsc;
if (is_not_set(rsc->flags, pe_rsc_unique)) {
failed = uber_parent(failed);
}
do_crm_log(actual_rc_i == EXECRA_NOT_INSTALLED ? LOG_NOTICE : LOG_ERR,
"Preventing %s from re-starting %s %s: operation %s failed '%s' (rc=%d)",
failed->id,
effective_node ? "on" : "anywhere in the cluster",
effective_node ? effective_node->details->uname : "",
task,
execra_code2string(actual_rc_i), actual_rc_i);
resource_location(failed, effective_node, -INFINITY, "hard-error", data_set);
if (is_probe) {
/* treat these like stops */
task = CRMD_ACTION_STOP;
task_status_i = LRM_OP_DONE;
crm_xml_add(xml_op, XML_ATTR_UNAME, node->details->uname);
if (actual_rc_i != EXECRA_NOT_INSTALLED
|| is_set(data_set->flags, pe_flag_symmetric_cluster)) {
if ((node->details->shutdown == FALSE) || (node->details->online == TRUE)) {
add_node_copy(data_set->failed, xml_op);
}
}
}
break;
case EXECRA_OK:
if (is_probe && target_rc == 7) {
task_status_i = LRM_OP_DONE;
crm_info("Operation %s found resource %s active on %s",
task, rsc->id, node->details->uname);
/* legacy code for pre-0.6.5 operations */
} else if (target_rc < 0 && interval > 0 && rsc->role == RSC_ROLE_MASTER) {
/* catch status ops that return 0 instead of 8 while they
* are supposed to be in master mode
*/
task_status_i = LRM_OP_ERROR;
}
break;
default:
if (task_status_i == LRM_OP_DONE) {
crm_info("Remapping %s (rc=%d) on %s to an ERROR",
id, actual_rc_i, node->details->uname);
task_status_i = LRM_OP_ERROR;
}
}
if (task_status_i == LRM_OP_ERROR
|| task_status_i == LRM_OP_TIMEOUT || task_status_i == LRM_OP_NOTSUPPORTED) {
action = custom_action(rsc, crm_strdup(id), task, NULL, TRUE, FALSE, data_set);
if (expired) {
crm_notice("Ignoring expired failure (calculated) %s (rc=%d, magic=%s) on %s",
id, actual_rc_i, magic, node->details->uname);
goto done;
} else if (action->on_fail == action_fail_ignore) {
crm_warn("Remapping %s (rc=%d) on %s to DONE: ignore",
id, actual_rc_i, node->details->uname);
task_status_i = LRM_OP_DONE;
set_bit(rsc->flags, pe_rsc_failure_ignored);
crm_xml_add(xml_op, XML_ATTR_UNAME, node->details->uname);
if ((node->details->shutdown == FALSE) || (node->details->online == TRUE)) {
add_node_copy(data_set->failed, xml_op);
}
}
}
switch (task_status_i) {
case LRM_OP_PENDING:
if (safe_str_eq(task, CRMD_ACTION_START)) {
set_bit(rsc->flags, pe_rsc_start_pending);
set_active(rsc);
} else if (safe_str_eq(task, CRMD_ACTION_PROMOTE)) {
rsc->role = RSC_ROLE_MASTER;
}
/*
* Intentionally ignoring pending migrate ops here;
* haven't decided if we need to do anything special
* with them yet...
*/
break;
case LRM_OP_DONE:
crm_trace("%s/%s completed on %s", rsc->id, task, node->details->uname);
if (actual_rc_i == EXECRA_NOT_RUNNING) {
clear_past_failure = TRUE;
} else if (safe_str_eq(task, CRMD_ACTION_START)) {
rsc->role = RSC_ROLE_STARTED;
clear_past_failure = TRUE;
} else if (safe_str_eq(task, CRMD_ACTION_STOP)) {
rsc->role = RSC_ROLE_STOPPED;
clear_past_failure = TRUE;
} else if (safe_str_eq(task, CRMD_ACTION_PROMOTE)) {
rsc->role = RSC_ROLE_MASTER;
clear_past_failure = TRUE;
} else if (safe_str_eq(task, CRMD_ACTION_DEMOTE)) {
/* Demote from Master does not clear an error */
rsc->role = RSC_ROLE_SLAVE;
} else if (safe_str_eq(task, CRMD_ACTION_MIGRATED)) {
rsc->role = RSC_ROLE_STARTED;
clear_past_failure = TRUE;
} else if (safe_str_eq(task, CRMD_ACTION_MIGRATE)) {
/*
* The normal sequence is (now): migrate_to(Src) -> migrate_from(Tgt) -> stop(Src)
*
* So if a migrate_to is followed by a stop, then we dont need to care what
* happended on the target node
*
* Without the stop, we need to look for a successful migrate_from.
* This would also imply we're no longer running on the source
*
* Without the stop, and without a migrate_from op we make sure the resource
* gets stopped on both source and target (assuming the target is up)
*
*/
int stop_id = 0;
xmlNode *stop_op =
find_lrm_op(rsc->id, CRMD_ACTION_STOP, node->details->id, NULL, data_set);
if (stop_op) {
crm_element_value_int(stop_op, XML_LRM_ATTR_CALLID, &stop_id);
}
if (stop_op == NULL || stop_id < task_id) {
int from_rc = 0, from_status = 0;
const char *migrate_source =
crm_element_value(xml_op, XML_LRM_ATTR_MIGRATE_SOURCE);
const char *migrate_target =
crm_element_value(xml_op, XML_LRM_ATTR_MIGRATE_TARGET);
node_t *target = pe_find_node(data_set->nodes, migrate_target);
xmlNode *migrate_from =
find_lrm_op(rsc->id, CRMD_ACTION_MIGRATED, migrate_target, migrate_source,
data_set);
rsc->role = RSC_ROLE_STARTED; /* can be master? */
if (migrate_from) {
crm_element_value_int(migrate_from, XML_LRM_ATTR_RC, &from_rc);
crm_element_value_int(migrate_from, XML_LRM_ATTR_OPSTATUS, &from_status);
crm_trace("%s op on %s exited with status=%d, rc=%d",
ID(migrate_from), migrate_target, from_status, from_rc);
}
if (migrate_from && from_rc == EXECRA_OK && from_status == LRM_OP_DONE) {
crm_trace("Detected dangling migration op: %s on %s", ID(xml_op),
migrate_source);
/* all good
* just need to arrange for the stop action to get sent
* but _without_ affecting the target somehow
*/
rsc->role = RSC_ROLE_STOPPED;
rsc->dangling_migrations = g_list_prepend(rsc->dangling_migrations, node);
} else if (migrate_from) { /* Failed */
crm_trace("Marking active on %s %p %d", migrate_target, target,
target->details->online);
if (target && target->details->online) {
native_add_running(rsc, target, data_set);
}
} else { /* Pending or complete but erased */
node_t *target = pe_find_node_id(data_set->nodes, migrate_target);
crm_trace("Marking active on %s %p %d", migrate_target, target,
target->details->online);
if (target && target->details->online) {
/* TODO: One day, figure out how to complete the migration
* For now, consider it active in both locations so it gets stopped everywhere
*/
native_add_running(rsc, target, data_set);
} else {
/* Consider it failed here - forces a restart, prevents migration */
set_bit_inplace(rsc->flags, pe_rsc_failed);
}
}
}
} else if (rsc->role < RSC_ROLE_STARTED) {
/* start, migrate_to and migrate_from will land here */
crm_trace("%s active on %s", rsc->id, node->details->uname);
set_active(rsc);
}
/* clear any previous failure actions */
if (clear_past_failure) {
switch (*on_fail) {
case action_fail_block:
case action_fail_stop:
case action_fail_fence:
case action_fail_migrate:
case action_fail_standby:
crm_trace("%s.%s is not cleared by a completed stop",
rsc->id, fail2text(*on_fail));
break;
case action_fail_ignore:
case action_fail_recover:
*on_fail = action_fail_ignore;
rsc->next_role = RSC_ROLE_UNKNOWN;
}
}
break;
case LRM_OP_ERROR:
case LRM_OP_TIMEOUT:
case LRM_OP_NOTSUPPORTED:
crm_warn("Processing failed op %s on %s: %s (%d)",
id, node->details->uname, execra_code2string(actual_rc_i), actual_rc_i);
crm_xml_add(xml_op, XML_ATTR_UNAME, node->details->uname);
if ((node->details->shutdown == FALSE) || (node->details->online == TRUE)) {
add_node_copy(data_set->failed, xml_op);
}
if (*on_fail < action->on_fail) {
*on_fail = action->on_fail;
}
if (safe_str_eq(task, CRMD_ACTION_STOP)) {
resource_location(rsc, node, -INFINITY, "__stop_fail__", data_set);
} else if (safe_str_eq(task, CRMD_ACTION_MIGRATED)) {
int stop_id = 0;
int migrate_id = 0;
const char *migrate_source = crm_element_value(xml_op, XML_LRM_ATTR_MIGRATE_SOURCE);
const char *migrate_target = crm_element_value(xml_op, XML_LRM_ATTR_MIGRATE_TARGET);
xmlNode *stop_op =
find_lrm_op(rsc->id, CRMD_ACTION_STOP, migrate_source, NULL, data_set);
xmlNode *migrate_op =
find_lrm_op(rsc->id, CRMD_ACTION_MIGRATE, migrate_source, migrate_target,
data_set);
if (stop_op) {
crm_element_value_int(stop_op, XML_LRM_ATTR_CALLID, &stop_id);
}
if (migrate_op) {
crm_element_value_int(migrate_op, XML_LRM_ATTR_CALLID, &migrate_id);
}
/* Get our state right */
rsc->role = RSC_ROLE_STARTED; /* can be master? */
if (stop_op == NULL || stop_id < migrate_id) {
node_t *source = pe_find_node(data_set->nodes, migrate_source);
if (source && source->details->online) {
native_add_running(rsc, source, data_set);
}
}
} else if (safe_str_eq(task, CRMD_ACTION_MIGRATE)) {
int stop_id = 0;
int migrate_id = 0;
const char *migrate_source = crm_element_value(xml_op, XML_LRM_ATTR_MIGRATE_SOURCE);
const char *migrate_target = crm_element_value(xml_op, XML_LRM_ATTR_MIGRATE_TARGET);
xmlNode *stop_op =
find_lrm_op(rsc->id, CRMD_ACTION_STOP, migrate_target, NULL, data_set);
xmlNode *migrate_op =
find_lrm_op(rsc->id, CRMD_ACTION_MIGRATED, migrate_target, migrate_source,
data_set);
if (stop_op) {
crm_element_value_int(stop_op, XML_LRM_ATTR_CALLID, &stop_id);
}
if (migrate_op) {
crm_element_value_int(migrate_op, XML_LRM_ATTR_CALLID, &migrate_id);
}
/* Get our state right */
rsc->role = RSC_ROLE_STARTED; /* can be master? */
if (stop_op == NULL || stop_id < migrate_id) {
node_t *target = pe_find_node(data_set->nodes, migrate_target);
crm_trace("Stop: %p %d, Migrated: %p %d", stop_op, stop_id, migrate_op,
migrate_id);
if (target && target->details->online) {
native_add_running(rsc, target, data_set);
}
} else if (migrate_op == NULL) {
/* Make sure it gets cleaned up, the stop may pre-date the migrate_from */
rsc->dangling_migrations = g_list_prepend(rsc->dangling_migrations, node);
}
} else if (safe_str_eq(task, CRMD_ACTION_PROMOTE)) {
rsc->role = RSC_ROLE_MASTER;
} else if (safe_str_eq(task, CRMD_ACTION_DEMOTE)) {
/*
* staying in role=master ends up putting the PE/TE into a loop
* setting role=slave is not dangerous because no master will be
* promoted until the failed resource has been fully stopped
*/
crm_warn("Forcing %s to stop after a failed demote action", rsc->id);
rsc->next_role = RSC_ROLE_STOPPED;
rsc->role = RSC_ROLE_SLAVE;
} else if (compare_version("2.0", op_version) > 0
&& safe_str_eq(task, CRMD_ACTION_START)) {
crm_warn("Compatibility handling for failed op %s on %s", id, node->details->uname);
resource_location(rsc, node, -INFINITY, "__legacy_start__", data_set);
}
if (rsc->role < RSC_ROLE_STARTED) {
set_active(rsc);
}
crm_trace("Resource %s: role=%s, unclean=%s, on_fail=%s, fail_role=%s",
rsc->id, role2text(rsc->role),
node->details->unclean ? "true" : "false",
fail2text(action->on_fail), role2text(action->fail_role));
if (action->fail_role != RSC_ROLE_STARTED && rsc->next_role < action->fail_role) {
rsc->next_role = action->fail_role;
}
if (action->fail_role == RSC_ROLE_STOPPED) {
int score = -INFINITY;
crm_err("Making sure %s doesn't come up again", rsc->id);
/* make sure it doesnt come up again */
g_hash_table_destroy(rsc->allowed_nodes);
rsc->allowed_nodes = node_hash_from_list(data_set->nodes);
g_hash_table_foreach(rsc->allowed_nodes, set_node_score, &score);
}
pe_free_action(action);
action = NULL;
break;
case LRM_OP_CANCELLED:
/* do nothing?? */
pe_err("Dont know what to do for cancelled ops yet");
break;
}
done:
crm_trace("Resource %s after %s: role=%s", rsc->id, task, role2text(rsc->role));
pe_free_action(action);
return TRUE;
}
gboolean
add_node_attrs(xmlNode * xml_obj, node_t * node, gboolean overwrite, pe_working_set_t * data_set)
{
g_hash_table_insert(node->details->attrs,
crm_strdup("#" XML_ATTR_UNAME), crm_strdup(node->details->uname));
g_hash_table_insert(node->details->attrs,
crm_strdup("#" XML_ATTR_ID), crm_strdup(node->details->id));
if (safe_str_eq(node->details->id, data_set->dc_uuid)) {
data_set->dc_node = node;
node->details->is_dc = TRUE;
g_hash_table_insert(node->details->attrs,
crm_strdup("#" XML_ATTR_DC), crm_strdup(XML_BOOLEAN_TRUE));
} else {
g_hash_table_insert(node->details->attrs,
crm_strdup("#" XML_ATTR_DC), crm_strdup(XML_BOOLEAN_FALSE));
}
unpack_instance_attributes(data_set->input, xml_obj, XML_TAG_ATTR_SETS, NULL,
node->details->attrs, NULL, overwrite, data_set->now);
return TRUE;
}
static GListPtr
extract_operations(const char *node, const char *rsc, xmlNode * rsc_entry, gboolean active_filter)
{
int counter = -1;
int stop_index = -1;
int start_index = -1;
xmlNode *rsc_op = NULL;
GListPtr gIter = NULL;
GListPtr op_list = NULL;
GListPtr sorted_op_list = NULL;
/* extract operations */
op_list = NULL;
sorted_op_list = NULL;
for (rsc_op = __xml_first_child(rsc_entry); rsc_op != NULL; rsc_op = __xml_next(rsc_op)) {
if (crm_str_eq((const char *)rsc_op->name, XML_LRM_TAG_RSC_OP, TRUE)) {
crm_xml_add(rsc_op, "resource", rsc);
crm_xml_add(rsc_op, XML_ATTR_UNAME, node);
op_list = g_list_prepend(op_list, rsc_op);
}
}
if (op_list == NULL) {
/* if there are no operations, there is nothing to do */
return NULL;
}
sorted_op_list = g_list_sort(op_list, sort_op_by_callid);
/* create active recurring operations as optional */
if (active_filter == FALSE) {
return sorted_op_list;
}
op_list = NULL;
calculate_active_ops(sorted_op_list, &start_index, &stop_index);
for (gIter = sorted_op_list; gIter != NULL; gIter = gIter->next) {
xmlNode *rsc_op = (xmlNode *) gIter->data;
counter++;
if (start_index < stop_index) {
crm_trace("Skipping %s: not active", ID(rsc_entry));
break;
} else if (counter < start_index) {
crm_trace("Skipping %s: old", ID(rsc_op));
continue;
}
op_list = g_list_append(op_list, rsc_op);
}
g_list_free(sorted_op_list);
return op_list;
}
GListPtr
find_operations(const char *rsc, const char *node, gboolean active_filter,
pe_working_set_t * data_set)
{
GListPtr output = NULL;
GListPtr intermediate = NULL;
xmlNode *tmp = NULL;
xmlNode *status = find_xml_node(data_set->input, XML_CIB_TAG_STATUS, TRUE);
const char *uname = NULL;
node_t *this_node = NULL;
xmlNode *node_state = NULL;
for (node_state = __xml_first_child(status); node_state != NULL;
node_state = __xml_next(node_state)) {
if (crm_str_eq((const char *)node_state->name, XML_CIB_TAG_STATE, TRUE)) {
uname = crm_element_value(node_state, XML_ATTR_UNAME);
if (node != NULL && safe_str_neq(uname, node)) {
continue;
}
this_node = pe_find_node(data_set->nodes, uname);
CRM_CHECK(this_node != NULL, continue);
determine_online_status(node_state, this_node, data_set);
if (this_node->details->online || is_set(data_set->flags, pe_flag_stonith_enabled)) {
/* offline nodes run no resources...
* unless stonith is enabled in which case we need to
* make sure rsc start events happen after the stonith
*/
xmlNode *lrm_rsc = NULL;
tmp = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE);
tmp = find_xml_node(tmp, XML_LRM_TAG_RESOURCES, FALSE);
for (lrm_rsc = __xml_first_child(tmp); lrm_rsc != NULL;
lrm_rsc = __xml_next(lrm_rsc)) {
if (crm_str_eq((const char *)lrm_rsc->name, XML_LRM_TAG_RESOURCE, TRUE)) {
const char *rsc_id = crm_element_value(lrm_rsc, XML_ATTR_ID);
if (rsc != NULL && safe_str_neq(rsc_id, rsc)) {
continue;
}
intermediate = extract_operations(uname, rsc_id, lrm_rsc, active_filter);
output = g_list_concat(output, intermediate);
}
}
}
}
}
return output;
}
diff --git a/lib/pengine/utils.c b/lib/pengine/utils.c
index 037b748ffc..a0c6dadd4e 100644
--- a/lib/pengine/utils.c
+++ b/lib/pengine/utils.c
@@ -1,1455 +1,1505 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <crm_internal.h>
#include <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/util.h>
#include <glib.h>
#include <crm/pengine/rules.h>
#include <utils.h>
pe_working_set_t *pe_dataset = NULL;
extern xmlNode *get_object_root(const char *object_type, xmlNode * the_root);
void print_str_str(gpointer key, gpointer value, gpointer user_data);
gboolean ghash_free_str_str(gpointer key, gpointer value, gpointer user_data);
void unpack_operation(action_t * action, xmlNode * xml_obj, pe_working_set_t * data_set);
node_t *
node_copy(node_t * this_node)
{
node_t *new_node = NULL;
CRM_CHECK(this_node != NULL, return NULL);
crm_malloc0(new_node, sizeof(node_t));
CRM_ASSERT(new_node != NULL);
crm_trace("Copying %p (%s) to %p", this_node, this_node->details->uname, new_node);
new_node->weight = this_node->weight;
new_node->fixed = this_node->fixed;
new_node->details = this_node->details;
return new_node;
}
/* any node in list1 or list2 and not in the other gets a score of -INFINITY */
void
node_list_exclude(GHashTable * hash, GListPtr list, gboolean merge_scores)
{
GHashTable *result = hash;
node_t *other_node = NULL;
GListPtr gIter = list;
GHashTableIter iter;
node_t *node = NULL;
g_hash_table_iter_init(&iter, hash);
while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
other_node = pe_find_node_id(list, node->details->id);
if (other_node == NULL) {
node->weight = -INFINITY;
} else if (merge_scores) {
node->weight = merge_weights(node->weight, other_node->weight);
}
}
for (; gIter != NULL; gIter = gIter->next) {
node_t *node = (node_t *) gIter->data;
other_node = pe_hash_table_lookup(result, node->details->id);
if (other_node == NULL) {
node_t *new_node = node_copy(node);
new_node->weight = -INFINITY;
g_hash_table_insert(result, (gpointer) new_node->details->id, new_node);
}
}
}
GHashTable *
node_hash_from_list(GListPtr list)
{
GListPtr gIter = list;
GHashTable *result = g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, g_hash_destroy_str);
for (; gIter != NULL; gIter = gIter->next) {
node_t *node = (node_t *) gIter->data;
node_t *n = node_copy(node);
g_hash_table_insert(result, (gpointer) n->details->id, n);
}
return result;
}
GListPtr
node_list_dup(GListPtr list1, gboolean reset, gboolean filter)
{
GListPtr result = NULL;
GListPtr gIter = list1;
for (; gIter != NULL; gIter = gIter->next) {
node_t *new_node = NULL;
node_t *this_node = (node_t *) gIter->data;
if (filter && this_node->weight < 0) {
continue;
}
new_node = node_copy(this_node);
if (reset) {
new_node->weight = 0;
}
if (new_node != NULL) {
result = g_list_prepend(result, new_node);
}
}
return result;
}
static gint
sort_node_uname(gconstpointer a, gconstpointer b)
{
const node_t *node_a = a;
const node_t *node_b = b;
return strcmp(node_a->details->uname, node_b->details->uname);
}
void
dump_node_scores_worker(int level, const char *file, const char *function, int line,
resource_t * rsc, const char *comment, GHashTable * nodes)
{
GHashTable *hash = nodes;
GHashTableIter iter;
node_t *node = NULL;
if (rsc) {
hash = rsc->allowed_nodes;
}
if (rsc && is_set(rsc->flags, pe_rsc_orphan)) {
/* Don't show the allocation scores for orphans */
return;
}
if (level == 0) {
/* For now we want this in sorted order to keep the regression tests happy */
GListPtr gIter = NULL;
GListPtr list = g_hash_table_get_values(hash);
list = g_list_sort(list, sort_node_uname);
gIter = list;
for (; gIter != NULL; gIter = gIter->next) {
node_t *node = (node_t *) gIter->data;
char *score = score2char(node->weight);
if (rsc) {
printf("%s: %s allocation score on %s: %s\n",
comment, rsc->id, node->details->uname, score);
} else {
printf("%s: %s = %s\n", comment, node->details->uname, score);
}
crm_free(score);
}
g_list_free(list);
} else if (hash) {
g_hash_table_iter_init(&iter, hash);
while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
char *score = score2char(node->weight);
if (rsc) {
do_crm_log_alias(LOG_TRACE, file, function, line, "%s: %s allocation score on %s: %s",
comment, rsc->id, node->details->uname, score);
} else {
do_crm_log_alias(LOG_TRACE, file, function, line+1, "%s: %s = %s", comment,
node->details->uname, score);
}
crm_free(score);
}
}
if (rsc && rsc->children) {
GListPtr gIter = NULL;
gIter = rsc->children;
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child = (resource_t *) gIter->data;
dump_node_scores_worker(level, file, function, line, child, comment, nodes);
}
}
}
static void
append_dump_text(gpointer key, gpointer value, gpointer user_data)
{
char **dump_text = user_data;
int len = 0;
char *new_text = NULL;
len = strlen(*dump_text) + strlen(" ") + strlen(key) + strlen("=") + strlen(value) + 1;
crm_malloc0(new_text, len);
sprintf(new_text, "%s %s=%s", *dump_text, (char *)key, (char *)value);
crm_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;
crm_malloc0(dump_text, 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);
}
crm_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;
crm_malloc0(dump_text, 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);
}
crm_free(dump_text);
}
gint
sort_rsc_index(gconstpointer a, gconstpointer b)
{
const resource_t *resource1 = (const resource_t *)a;
const resource_t *resource2 = (const resource_t *)b;
if (a == NULL && b == NULL) {
return 0;
}
if (a == NULL) {
return 1;
}
if (b == NULL) {
return -1;
}
if (resource1->sort_index > resource2->sort_index) {
return -1;
}
if (resource1->sort_index < resource2->sort_index) {
return 1;
}
return 0;
}
gint
sort_rsc_priority(gconstpointer a, gconstpointer b)
{
const resource_t *resource1 = (const resource_t *)a;
const resource_t *resource2 = (const resource_t *)b;
if (a == NULL && b == NULL) {
return 0;
}
if (a == NULL) {
return 1;
}
if (b == NULL) {
return -1;
}
if (resource1->priority > resource2->priority) {
return -1;
}
if (resource1->priority < resource2->priority) {
return 1;
}
return 0;
}
action_t *
custom_action(resource_t * rsc, char *key, const char *task,
node_t * on_node, gboolean optional, gboolean save_action,
pe_working_set_t * data_set)
{
action_t *action = NULL;
GListPtr possible_matches = NULL;
CRM_CHECK(key != NULL, return NULL);
CRM_CHECK(task != NULL, return NULL);
if (save_action && rsc != NULL) {
possible_matches = find_actions(rsc->actions, key, on_node);
}
if (possible_matches != NULL) {
crm_free(key);
if (g_list_length(possible_matches) > 1) {
pe_warn("Action %s for %s on %s exists %d times",
task, rsc ? rsc->id : "<NULL>",
on_node ? on_node->details->uname : "<NULL>", g_list_length(possible_matches));
}
action = g_list_nth_data(possible_matches, 0);
crm_trace("Found existing action (%d) %s for %s on %s",
action->id, task, rsc ? rsc->id : "<NULL>",
on_node ? on_node->details->uname : "<NULL>");
g_list_free(possible_matches);
}
if (action == NULL) {
if (save_action) {
crm_trace("Creating%s action %d: %s for %s on %s",
optional ? "" : " manditory", data_set->action_id, key,
rsc ? rsc->id : "<NULL>", on_node ? on_node->details->uname : "<NULL>");
}
crm_malloc0(action, sizeof(action_t));
if (save_action) {
action->id = data_set->action_id++;
} else {
action->id = 0;
}
action->rsc = rsc;
CRM_ASSERT(task != NULL);
action->task = crm_strdup(task);
if (on_node) {
action->node = node_copy(on_node);
}
action->uuid = key;
set_bit_inplace(action->flags, pe_action_failure_is_fatal);
set_bit_inplace(action->flags, pe_action_runnable);
if (optional) {
set_bit_inplace(action->flags, pe_action_optional);
} else {
clear_bit_inplace(action->flags, pe_action_optional);
}
/*
Implied by crm_malloc0()...
action->actions_before = NULL;
action->actions_after = NULL;
action->pseudo = FALSE;
action->dumped = FALSE;
action->processed = FALSE;
action->seen_count = 0;
*/
action->extra = g_hash_table_new_full(crm_str_hash, g_str_equal, free, free);
action->meta = g_hash_table_new_full(crm_str_hash, g_str_equal, free, free);
if (save_action) {
data_set->actions = g_list_prepend(data_set->actions, action);
}
if (rsc != NULL) {
action->op_entry = find_rsc_op_entry(rsc, key);
unpack_operation(action, action->op_entry, data_set);
if (save_action) {
rsc->actions = g_list_prepend(rsc->actions, action);
}
}
if (save_action) {
crm_trace("Action %d created", action->id);
}
}
if (optional == FALSE && (action->flags & pe_action_optional)) {
crm_trace("Action %d (%s) marked manditory", action->id, action->uuid);
clear_bit_inplace(action->flags, pe_action_optional);
}
if (rsc != NULL) {
enum action_tasks a_task = text2task(action->task);
int warn_level = LOG_DEBUG_3;
if (save_action) {
warn_level = LOG_WARNING;
}
if (is_set(action->flags, pe_action_have_node_attrs) == FALSE
&& action->node != NULL && action->op_entry != NULL) {
set_bit_inplace(action->flags, 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) {
clear_bit_inplace(action->flags, 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);
set_bit_inplace(action->flags, pe_action_optional);
/* action->runnable = FALSE; */
} else if (action->node->details->online == FALSE) {
clear_bit_inplace(action->flags, pe_action_runnable);
do_crm_log(warn_level, "Action %s on %s is unrunnable (offline)",
action->uuid, action->node->details->uname);
if (is_set(action->rsc->flags, pe_rsc_managed)
&& save_action && a_task == stop_rsc) {
do_crm_log(warn_level, "Marking node %s unclean", action->node->details->uname);
action->node->details->unclean = TRUE;
}
} else if (action->node->details->pending) {
clear_bit_inplace(action->flags, pe_action_runnable);
do_crm_log(warn_level, "Action %s on %s is unrunnable (pending)",
action->uuid, action->node->details->uname);
} else if (action->needs == rsc_req_nothing) {
crm_trace("Action %s doesnt require anything", action->uuid);
set_bit_inplace(action->flags, 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) {
clear_bit_inplace(action->flags, pe_action_runnable);
crm_debug("%s\t%s (cancelled : quorum)", action->node->details->uname, action->uuid);
} else if (is_set(data_set->flags, pe_flag_have_quorum) == FALSE
&& data_set->no_quorum_policy == no_quorum_freeze) {
crm_trace("Check resource is already active");
if (rsc->fns->active(rsc, TRUE) == FALSE) {
clear_bit_inplace(action->flags, pe_action_runnable);
crm_debug("%s\t%s (cancelled : quorum freeze)",
action->node->details->uname, action->uuid);
}
} else {
crm_trace("Action %s is runnable", action->uuid);
set_bit_inplace(action->flags, 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;
}
}
}
return action;
}
void
unpack_operation(action_t * action, xmlNode * xml_obj, pe_working_set_t * data_set)
{
int value_i = 0;
unsigned long long interval = 0;
unsigned long long start_delay = 0;
char *value_ms = NULL;
const char *class = NULL;
const char *value = NULL;
const char *field = NULL;
CRM_CHECK(action->rsc != NULL, return);
unpack_instance_attributes(data_set->input, data_set->op_defaults, XML_TAG_META_SETS, NULL,
action->meta, NULL, FALSE, data_set->now);
if (xml_obj) {
xmlAttrPtr xIter = NULL;
for (xIter = xml_obj->properties; xIter; xIter = xIter->next) {
const char *prop_name = (const char *)xIter->name;
const char *prop_value = crm_element_value(xml_obj, prop_name);
g_hash_table_replace(action->meta, crm_strdup(prop_name), crm_strdup(prop_value));
}
}
unpack_instance_attributes(data_set->input, xml_obj, XML_TAG_META_SETS,
NULL, action->meta, NULL, FALSE, data_set->now);
unpack_instance_attributes(data_set->input, xml_obj, XML_TAG_ATTR_SETS,
NULL, action->meta, NULL, FALSE, data_set->now);
g_hash_table_remove(action->meta, "id");
class = g_hash_table_lookup(action->rsc->meta, "class");
value = g_hash_table_lookup(action->meta, "requires");
if (safe_str_eq(class, "stonith")) {
action->needs = rsc_req_nothing;
value = "nothing (fencing op)";
} else if (safe_str_eq(value, "nothing")) {
action->needs = rsc_req_nothing;
} else if (safe_str_eq(value, "quorum")) {
action->needs = rsc_req_quorum;
} else if (is_set(data_set->flags, pe_flag_stonith_enabled)
&& safe_str_eq(value, "fencing")) {
action->needs = rsc_req_stonith;
} else {
if (value) {
crm_config_err("Invalid value for %s->requires: %s%s",
action->rsc->id, value,
is_set(data_set->flags,
pe_flag_stonith_enabled) ? "" : " (stonith-enabled=false)");
}
if (safe_str_eq(action->task, CRMD_ACTION_STATUS)
|| safe_str_eq(action->task, CRMD_ACTION_NOTIFY)) {
action->needs = rsc_req_nothing;
value = "nothing (default)";
} else if (data_set->no_quorum_policy == no_quorum_stop
&& safe_str_neq(action->task, CRMD_ACTION_START)) {
action->needs = rsc_req_nothing;
value = "nothing (default)";
} else if (is_set(data_set->flags, pe_flag_stonith_enabled)) {
action->needs = rsc_req_stonith;
value = "fencing (default)";
} else {
action->needs = rsc_req_quorum;
value = "quorum (default)";
}
}
crm_trace("\tAction %s requires: %s", action->task, value);
value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ON_FAIL);
if (safe_str_eq(action->task, CRMD_ACTION_STOP)
&& safe_str_eq(value, "standby")) {
crm_config_err("on-fail=standby is not allowed for stop actions: %s", action->rsc->id);
value = NULL;
}
if (value == NULL) {
} else if (safe_str_eq(value, "block")) {
action->on_fail = action_fail_block;
} else if (safe_str_eq(value, "fence")) {
action->on_fail = action_fail_fence;
value = "node fencing";
if (is_set(data_set->flags, pe_flag_stonith_enabled) == FALSE) {
crm_config_err("Specifying on_fail=fence and" " stonith-enabled=false makes no sense");
action->on_fail = action_fail_stop;
action->fail_role = RSC_ROLE_STOPPED;
value = "stop resource";
}
} else if (safe_str_eq(value, "standby")) {
action->on_fail = action_fail_standby;
value = "node standby";
} else if (safe_str_eq(value, "ignore")
|| safe_str_eq(value, "nothing")) {
action->on_fail = action_fail_ignore;
value = "ignore";
} else if (safe_str_eq(value, "migrate")) {
action->on_fail = action_fail_migrate;
value = "force migration";
} else if (safe_str_eq(value, "stop")) {
action->on_fail = action_fail_stop;
action->fail_role = RSC_ROLE_STOPPED;
value = "stop resource";
} else if (safe_str_eq(value, "restart")) {
action->on_fail = action_fail_recover;
value = "restart (and possibly migrate)";
} else {
pe_err("Resource %s: Unknown failure type (%s)", action->rsc->id, value);
value = NULL;
}
/* defaults */
if (value == NULL && safe_str_eq(action->task, CRMD_ACTION_STOP)) {
if (is_set(data_set->flags, pe_flag_stonith_enabled)) {
action->on_fail = action_fail_fence;
value = "resource fence (default)";
} else {
action->on_fail = action_fail_block;
value = "resource block (default)";
}
} else if (value == NULL) {
action->on_fail = action_fail_recover;
value = "restart (and possibly migrate) (default)";
}
crm_trace("\t%s failure handling: %s", action->task, value);
value = NULL;
if (xml_obj != NULL) {
value = g_hash_table_lookup(action->meta, "role_after_failure");
}
if (value != NULL && action->fail_role == RSC_ROLE_UNKNOWN) {
action->fail_role = text2role(value);
}
/* defaults */
if (action->fail_role == RSC_ROLE_UNKNOWN) {
if (safe_str_eq(action->task, CRMD_ACTION_PROMOTE)) {
action->fail_role = RSC_ROLE_SLAVE;
} else {
action->fail_role = RSC_ROLE_STARTED;
}
}
crm_trace("\t%s failure results in: %s", action->task, role2text(action->fail_role));
field = XML_LRM_ATTR_INTERVAL;
value = g_hash_table_lookup(action->meta, field);
if (value != NULL) {
interval = crm_get_interval(value);
if (interval > 0) {
value_ms = crm_itoa(interval);
g_hash_table_replace(action->meta, crm_strdup(field), value_ms);
} else {
g_hash_table_remove(action->meta, field);
}
}
field = XML_OP_ATTR_START_DELAY;
value = g_hash_table_lookup(action->meta, field);
if (value != NULL) {
value_i = crm_get_msec(value);
if (value_i < 0) {
value_i = 0;
}
start_delay = value_i;
value_ms = crm_itoa(value_i);
g_hash_table_replace(action->meta, crm_strdup(field), value_ms);
} else if (interval > 0 && g_hash_table_lookup(action->meta, XML_OP_ATTR_ORIGIN)) {
char *date_str = NULL;
char *date_str_mutable = NULL;
ha_time_t *origin = NULL;
value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ORIGIN);
date_str = crm_strdup(value);
date_str_mutable = date_str;
origin = parse_date(&date_str_mutable);
crm_free(date_str);
if (origin == NULL) {
crm_config_err("Operation %s contained an invalid " XML_OP_ATTR_ORIGIN ": %s",
ID(xml_obj), value);
} else {
ha_time_t *delay = NULL;
int rc = compare_date(origin, data_set->now);
unsigned long long delay_s = 0;
while (rc < 0) {
add_seconds(origin, interval / 1000);
rc = compare_date(origin, data_set->now);
}
delay = subtract_time(origin, data_set->now);
delay_s = date_in_seconds(delay);
/* log_date(LOG_DEBUG_5, "delay", delay, ha_log_date|ha_log_time|ha_log_local); */
crm_info("Calculated a start delay of %llus for %s", delay_s, ID(xml_obj));
g_hash_table_replace(action->meta, crm_strdup(XML_OP_ATTR_START_DELAY),
crm_itoa(delay_s * 1000));
start_delay = delay_s * 1000;
free_ha_date(origin);
free_ha_date(delay);
}
}
field = XML_ATTR_TIMEOUT;
value = g_hash_table_lookup(action->meta, field);
if (value == NULL) {
value = pe_pref(data_set->config_hash, "default-action-timeout");
}
value_i = crm_get_msec(value);
if (value_i < 0) {
value_i = 0;
}
value_i += start_delay;
value_ms = crm_itoa(value_i);
g_hash_table_replace(action->meta, crm_strdup(field), value_ms);
}
xmlNode *
find_rsc_op_entry(resource_t * rsc, const char *key)
{
int number = 0;
gboolean do_retry = TRUE;
char *local_key = NULL;
const char *name = NULL;
const char *value = NULL;
const char *interval = NULL;
char *match_key = NULL;
xmlNode *op = NULL;
xmlNode *operation = NULL;
retry:
for (operation = __xml_first_child(rsc->ops_xml); operation != NULL;
operation = __xml_next(operation)) {
if (crm_str_eq((const char *)operation->name, "op", TRUE)) {
name = crm_element_value(operation, "name");
interval = crm_element_value(operation, XML_LRM_ATTR_INTERVAL);
value = crm_element_value(operation, "enabled");
if (value && crm_is_true(value) == FALSE) {
continue;
}
number = crm_get_interval(interval);
if (number < 0) {
continue;
}
match_key = generate_op_key(rsc->id, name, number);
if (safe_str_eq(key, match_key)) {
op = operation;
}
crm_free(match_key);
if (op != NULL) {
crm_free(local_key);
return op;
}
}
}
crm_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;
}
void
print_node(const char *pre_text, node_t * node, gboolean details)
{
if (node == NULL) {
crm_trace("%s%s: <NULL>", pre_text == NULL ? "" : pre_text, pre_text == NULL ? "" : ": ");
return;
}
crm_trace("%s%s%sNode %s: (weight=%d, fixed=%s)",
pre_text == NULL ? "" : pre_text,
pre_text == NULL ? "" : ": ",
node->details ==
NULL ? "error " : node->details->online ? "" : "Unavailable/Unclean ",
node->details->uname, node->weight, node->fixed ? "True" : "False");
if (details && node != NULL && node->details != NULL) {
char *pe_mutable = crm_strdup("\t\t");
GListPtr gIter = node->details->running_rsc;
crm_trace("\t\t===Node Attributes");
g_hash_table_foreach(node->details->attrs, print_str_str, pe_mutable);
crm_free(pe_mutable);
crm_trace("\t\t=== Resources");
for (; gIter != NULL; gIter = gIter->next) {
resource_t *rsc = (resource_t *) gIter->data;
print_resource(LOG_DEBUG_4, "\t\t", rsc, FALSE);
}
}
}
/*
* Used by the HashTable for-loop
*/
void
print_str_str(gpointer key, gpointer value, gpointer user_data)
{
crm_trace("%s%s %s ==> %s",
user_data == NULL ? "" : (char *)user_data,
user_data == NULL ? "" : ": ", (char *)key, (char *)value);
}
void
print_resource(int log_level, const char *pre_text, resource_t * rsc, gboolean details)
{
long options = pe_print_log;
if (rsc == NULL) {
do_crm_log(log_level - 1, "%s%s: <NULL>",
pre_text == NULL ? "" : pre_text, pre_text == NULL ? "" : ": ");
return;
}
if (details) {
options |= pe_print_details;
}
rsc->fns->print(rsc, pre_text, options, &log_level);
}
void
pe_free_action(action_t * action)
{
if (action == NULL) {
return;
}
slist_basic_destroy(action->actions_before); /* action_warpper_t* */
slist_basic_destroy(action->actions_after); /* action_warpper_t* */
if (action->extra) {
g_hash_table_destroy(action->extra);
}
if (action->meta) {
g_hash_table_destroy(action->meta);
}
crm_free(action->task);
crm_free(action->uuid);
crm_free(action->node);
crm_free(action);
}
GListPtr
find_recurring_actions(GListPtr input, node_t * not_on_node)
{
const char *value = NULL;
GListPtr result = NULL;
GListPtr gIter = input;
CRM_CHECK(input != NULL, return NULL);
for (; gIter != NULL; gIter = gIter->next) {
action_t *action = (action_t *) gIter->data;
value = g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL);
if (value == NULL) {
/* skip */
} else if (safe_str_eq(value, "0")) {
/* skip */
} else if (safe_str_eq(CRMD_ACTION_CANCEL, action->task)) {
/* skip */
} else if (not_on_node == NULL) {
crm_trace("(null) Found: %s", action->uuid);
result = g_list_prepend(result, action);
} else if (action->node == NULL) {
/* skip */
} else if (action->node->details != not_on_node->details) {
crm_trace("Found: %s", action->uuid);
result = g_list_prepend(result, action);
}
}
return result;
}
enum action_tasks
get_complex_task(resource_t * rsc, const char *name, gboolean allow_non_atomic)
{
enum action_tasks task = text2task(name);
if (rsc == NULL) {
return task;
} else if (allow_non_atomic == FALSE || rsc->variant == pe_native) {
switch (task) {
case stopped_rsc:
case started_rsc:
case action_demoted:
case action_promoted:
crm_trace("Folding %s back into its atomic counterpart for %s", name, rsc->id);
return task - 1;
break;
default:
break;
}
}
return task;
}
action_t *
find_first_action(GListPtr input, const char *uuid, const char *task, node_t * on_node)
{
GListPtr gIter = NULL;
CRM_CHECK(uuid || task, return NULL);
for (gIter = input; gIter != NULL; gIter = gIter->next) {
action_t *action = (action_t *) gIter->data;
if (uuid != NULL && safe_str_neq(uuid, action->uuid)) {
continue;
} else if (task != NULL && safe_str_neq(task, action->task)) {
continue;
} else if (on_node == NULL) {
return action;
} else if (action->node == NULL) {
continue;
} else if (on_node->details == action->node->details) {
return action;
}
}
return NULL;
}
GListPtr
find_actions(GListPtr input, const char *key, node_t * on_node)
{
GListPtr gIter = input;
GListPtr result = NULL;
CRM_CHECK(key != NULL, return NULL);
for (; gIter != NULL; gIter = gIter->next) {
action_t *action = (action_t *) gIter->data;
crm_trace("Matching %s against %s", key, action->uuid);
if (safe_str_neq(key, action->uuid)) {
continue;
} else if (on_node == NULL) {
result = g_list_prepend(result, action);
} else if (action->node == NULL) {
/* skip */
crm_trace("While looking for %s action on %s, "
"found an unallocated one. Assigning"
" it to the requested node...", key, on_node->details->uname);
action->node = node_copy(on_node);
result = g_list_prepend(result, action);
} else if (on_node->details == action->node->details) {
result = g_list_prepend(result, action);
}
}
return result;
}
GListPtr
find_actions_exact(GListPtr input, const char *key, node_t * on_node)
{
GListPtr gIter = input;
GListPtr result = NULL;
CRM_CHECK(key != NULL, return NULL);
for (; gIter != NULL; gIter = gIter->next) {
action_t *action = (action_t *) gIter->data;
crm_trace("Matching %s against %s", key, action->uuid);
if (safe_str_neq(key, action->uuid)) {
crm_trace("Key mismatch: %s vs. %s", key, action->uuid);
continue;
} else if (on_node == NULL || action->node == NULL) {
crm_trace("on_node=%p, action->node=%p", on_node, action->node);
continue;
} else if (safe_str_eq(on_node->details->id, action->node->details->id)) {
result = g_list_prepend(result, action);
}
crm_trace("Node mismatch: %s vs. %s", on_node->details->id, action->node->details->id);
}
return result;
}
static void
resource_node_score(resource_t * rsc, node_t * node, int score, const char *tag)
{
node_t *match = NULL;
if (rsc->children) {
GListPtr gIter = rsc->children;
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t *) gIter->data;
resource_node_score(child_rsc, node, score, tag);
}
}
crm_trace("Setting %s for %s on %s: %d", tag, rsc->id, node->details->uname, score);
match = pe_hash_table_lookup(rsc->allowed_nodes, node->details->id);
if (match == NULL) {
match = node_copy(node);
match->weight = merge_weights(score, node->weight);
g_hash_table_insert(rsc->allowed_nodes, (gpointer) match->details->id, match);
}
match->weight = merge_weights(match->weight, score);
}
void
resource_location(resource_t * rsc, node_t * node, int score, const char *tag,
pe_working_set_t * data_set)
{
if (node != NULL) {
resource_node_score(rsc, node, score, tag);
} else if (data_set != NULL) {
GListPtr gIter = data_set->nodes;
for (; gIter != NULL; gIter = gIter->next) {
node_t *node = (node_t *) gIter->data;
resource_node_score(rsc, node, score, tag);
}
} else {
GHashTableIter iter;
node_t *node = NULL;
g_hash_table_iter_init(&iter, rsc->allowed_nodes);
while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
resource_node_score(rsc, node, score, tag);
}
}
if (node == NULL && score == -INFINITY) {
if (rsc->allocated_to) {
crm_info("Deallocating %s from %s", rsc->id, rsc->allocated_to->details->uname);
crm_free(rsc->allocated_to);
rsc->allocated_to = NULL;
}
}
}
#define sort_return(an_int, why) do { \
crm_free(a_uuid); \
crm_free(b_uuid); \
crm_trace("%s (%d) %c %s (%d) : %s", \
a_xml_id, a_call_id, an_int>0?'>':an_int<0?'<':'=', \
b_xml_id, b_call_id, why); \
return an_int; \
} while(0)
gint
sort_op_by_callid(gconstpointer a, gconstpointer b)
{
int a_call_id = -1;
int b_call_id = -1;
char *a_uuid = NULL;
char *b_uuid = NULL;
const xmlNode *xml_a = a;
const xmlNode *xml_b = b;
const char *a_xml_id = crm_element_value_const(xml_a, XML_ATTR_ID);
const char *b_xml_id = crm_element_value_const(xml_b, XML_ATTR_ID);
if (safe_str_eq(a_xml_id, b_xml_id)) {
/* We have duplicate lrm_rsc_op entries in the status
* section which is unliklely to be a good thing
* - we can handle it easily enough, but we need to get
* to the bottom of why its happening.
*/
pe_err("Duplicate lrm_rsc_op entries named %s", a_xml_id);
sort_return(0, "duplicate");
}
crm_element_value_const_int(xml_a, XML_LRM_ATTR_CALLID, &a_call_id);
crm_element_value_const_int(xml_b, XML_LRM_ATTR_CALLID, &b_call_id);
if (a_call_id == -1 && b_call_id == -1) {
/* both are pending ops so it doesnt matter since
* stops are never pending
*/
sort_return(0, "pending");
} else if (a_call_id >= 0 && a_call_id < b_call_id) {
sort_return(-1, "call id");
} else if (b_call_id >= 0 && a_call_id > b_call_id) {
sort_return(1, "call id");
} else if (b_call_id >= 0 && a_call_id == b_call_id) {
/*
* The op and last_failed_op are the same
* Order on last-rc-change
*/
int last_a = -1;
int last_b = -1;
crm_element_value_const_int(xml_a, "last-rc-change", &last_a);
crm_element_value_const_int(xml_b, "last-rc-change", &last_b);
if (last_a >= 0 && last_a < last_b) {
sort_return(-1, "rc-change");
} else if (last_b >= 0 && last_a > last_b) {
sort_return(1, "rc-change");
}
sort_return(0, "rc-change");
} else {
/* One of the inputs is a pending operation
* Attempt to use XML_ATTR_TRANSITION_MAGIC to determine its age relative to the other
*/
int a_id = -1;
int b_id = -1;
int dummy = -1;
const char *a_magic = crm_element_value_const(xml_a, XML_ATTR_TRANSITION_MAGIC);
const char *b_magic = crm_element_value_const(xml_b, XML_ATTR_TRANSITION_MAGIC);
CRM_CHECK(a_magic != NULL && b_magic != NULL, sort_return(0, "No magic"));
CRM_CHECK(decode_transition_magic(a_magic, &a_uuid, &a_id, &dummy, &dummy, &dummy, &dummy),
sort_return(0, "bad magic a"));
CRM_CHECK(decode_transition_magic(b_magic, &b_uuid, &b_id, &dummy, &dummy, &dummy, &dummy),
sort_return(0, "bad magic b"));
/* try and determin the relative age of the operation...
* some pending operations (ie. a start) may have been supuerceeded
* by a subsequent stop
*
* [a|b]_id == -1 means its a shutdown operation and _always_ comes last
*/
if (safe_str_neq(a_uuid, b_uuid) || a_id == b_id) {
/*
* some of the logic in here may be redundant...
*
* if the UUID from the TE doesnt match then one better
* be a pending operation.
* pending operations dont survive between elections and joins
* because we query the LRM directly
*/
if (b_call_id == -1) {
sort_return(-1, "transition + call");
} else if (a_call_id == -1) {
sort_return(1, "transition + call");
}
} else if ((a_id >= 0 && a_id < b_id) || b_id == -1) {
sort_return(-1, "transition");
} else if ((b_id >= 0 && a_id > b_id) || a_id == -1) {
sort_return(1, "transition");
}
}
/* we should never end up here */
CRM_CHECK(FALSE, sort_return(0, "default"));
}
time_t
get_timet_now(pe_working_set_t * data_set)
{
time_t now = 0;
if (data_set && data_set->now) {
now = data_set->now->tm_now;
}
if (now == 0) {
/* eventually we should convert data_set->now into time_tm
* for now, its only triggered by PE regression tests
*/
now = time(NULL);
crm_crit("Defaulting to 'now'");
if (data_set && data_set->now) {
data_set->now->tm_now = now;
}
}
return now;
}
struct fail_search {
resource_t *rsc;
int count;
long long last;
char *key;
};
static void
get_failcount_by_prefix(gpointer key_p, gpointer value, gpointer user_data)
{
struct fail_search *search = user_data;
const char *key = key_p;
const char *match = strstr(key, search->key);
if (match) {
if (strstr(key, "last-failure-") == key && (key + 13) == match) {
search->last = crm_int_helper(value, NULL);
} else if (strstr(key, "fail-count-") == key && (key + 11) == match) {
search->count += char2score(value);
}
}
}
int
get_failcount(node_t * node, resource_t * rsc, int *last_failure, pe_working_set_t * data_set)
{
struct fail_search search = { rsc, 0, 0, NULL };
search.key = crm_strdup(rsc->id);
if (is_not_set(rsc->flags, pe_rsc_unique)) {
int lpc = 0;
search.rsc = uber_parent(rsc);
/* Strip the clone incarnation */
for (lpc = strlen(search.key); lpc > 0; lpc--) {
if (search.key[lpc] == ':') {
search.key[lpc + 1] = 0;
break;
}
}
g_hash_table_foreach(node->details->attrs, get_failcount_by_prefix, &search);
} else {
/* Optimize the "normal" case */
char *key = NULL;
const char *value = NULL;
key = crm_concat("fail-count", rsc->id, '-');
value = g_hash_table_lookup(node->details->attrs, key);
search.count = char2score(value);
crm_free(key);
key = crm_concat("last-failure", rsc->id, '-');
value = g_hash_table_lookup(node->details->attrs, key);
search.last = crm_int_helper(value, NULL);
crm_free(key);
}
if (search.count != 0 && search.last != 0 && rsc->failure_timeout) {
if (last_failure) {
*last_failure = search.last;
}
if (search.last > 0) {
time_t now = get_timet_now(data_set);
if (now > (search.last + rsc->failure_timeout)) {
crm_notice("Failcount for %s on %s has expired (limit was %ds)",
search.rsc->id, node->details->uname, rsc->failure_timeout);
search.count = 0;
}
}
}
if (search.count != 0) {
char *score = score2char(search.count);
crm_info("%s has failed %s times on %s",
search.rsc->id, score, node->details->uname);
crm_free(score);
}
crm_free(search.key);
return search.count;
}
gboolean
get_target_role(resource_t * rsc, enum rsc_role_e * role)
{
enum rsc_role_e local_role = RSC_ROLE_UNKNOWN;
const char *value = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE);
CRM_CHECK(role != NULL, return FALSE);
if (value == NULL || safe_str_eq("started", value)
|| safe_str_eq("default", value)) {
return FALSE;
}
local_role = text2role(value);
if (local_role == RSC_ROLE_UNKNOWN) {
crm_config_err("%s: Unknown value for %s: %s", rsc->id, XML_RSC_ATTR_TARGET_ROLE, value);
return FALSE;
} else if (local_role > RSC_ROLE_STARTED) {
if (uber_parent(rsc)->variant == pe_master) {
if (local_role > RSC_ROLE_SLAVE) {
/* This is what we'd do anyway, just leave the default to avoid messing up the placement algorithm */
return FALSE;
}
} else {
crm_config_err("%s is not part of a master/slave resource, a %s of '%s' makes no sense",
rsc->id, XML_RSC_ATTR_TARGET_ROLE, value);
return FALSE;
}
}
*role = local_role;
return TRUE;
}
gboolean
order_actions(action_t * lh_action, action_t * rh_action, enum pe_ordering order)
{
GListPtr gIter = NULL;
action_wrapper_t *wrapper = NULL;
GListPtr list = NULL;
if (order == pe_order_none) {
return FALSE;
}
if (lh_action == NULL || rh_action == NULL) {
return FALSE;
}
crm_trace("Ordering Action %s before %s", lh_action->uuid, rh_action->uuid);
/* Filter dups, otherwise update_action_states() has too much work to do */
gIter = lh_action->actions_after;
for (; gIter != NULL; gIter = gIter->next) {
action_wrapper_t *after = (action_wrapper_t *) gIter->data;
if (after->action == rh_action && (after->type & order)) {
return FALSE;
}
}
crm_malloc0(wrapper, 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; */
crm_malloc0(wrapper, sizeof(action_wrapper_t));
wrapper->action = lh_action;
wrapper->type = order;
list = rh_action->actions_before;
list = g_list_prepend(list, wrapper);
rh_action->actions_before = list;
return TRUE;
}
action_t *
get_pseudo_op(const char *name, pe_working_set_t * data_set)
{
action_t *op = NULL;
const char *op_s = name;
GListPtr possible_matches = NULL;
possible_matches = find_actions(data_set->actions, name, NULL);
if (possible_matches != NULL) {
if (g_list_length(possible_matches) > 1) {
pe_warn("Action %s exists %d times", name, g_list_length(possible_matches));
}
op = g_list_nth_data(possible_matches, 0);
g_list_free(possible_matches);
} else {
op = custom_action(NULL, crm_strdup(op_s), op_s, NULL, TRUE, TRUE, data_set);
set_bit_inplace(op->flags, pe_action_pseudo);
set_bit_inplace(op->flags, pe_action_runnable);
}
return op;
}
+
+void
+destroy_ticket(gpointer data)
+{
+ ticket_t *ticket = data;
+
+ if (ticket->state) {
+ g_hash_table_destroy(ticket->state);
+ }
+ crm_free(ticket->id);
+ crm_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) {
+
+ crm_malloc0(ticket, sizeof(ticket_t));
+ if (ticket == NULL) {
+ crm_err("Cannot allocate ticket '%s'", ticket_id);
+ return NULL;
+ }
+
+ crm_trace("Creaing ticket entry for %s", ticket_id);
+
+ ticket->id = crm_strdup(ticket_id);
+ ticket->granted = FALSE;
+ ticket->last_granted = -1;
+ ticket->standby = FALSE;
+ ticket->state = g_hash_table_new_full(crm_str_hash, g_str_equal,
+ g_hash_destroy_str, g_hash_destroy_str);
+
+ g_hash_table_insert(data_set->tickets, crm_strdup(ticket->id), ticket);
+ }
+
+ return ticket;
+}
diff --git a/lib/pengine/utils.h b/lib/pengine/utils.h
index c5cfe62be8..7e7d92084e 100644
--- a/lib/pengine/utils.h
+++ b/lib/pengine/utils.h
@@ -1,151 +1,154 @@
/*
* 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
*/
#ifndef PE_UTILS__H
# define PE_UTILS__H
# include <crm/pengine/common.h>
# include <crm/pengine/status.h>
extern pe_working_set_t *pe_dataset;
extern node_t *node_copy(node_t * this_node);
extern time_t get_timet_now(pe_working_set_t * data_set);
extern int get_failcount(node_t * node, resource_t * rsc, int *last_failure,
pe_working_set_t * data_set);
/* Binary like operators for lists of nodes */
extern void node_list_exclude(GHashTable * list, GListPtr list2, gboolean merge_scores);
extern GListPtr node_list_dup(GListPtr list, gboolean reset, gboolean filter);
extern GListPtr node_list_from_hash(GHashTable * hash, gboolean reset, gboolean filter);
extern GHashTable *node_hash_from_list(GListPtr list);
static inline gpointer
pe_hash_table_lookup(GHashTable * hash, gconstpointer key)
{
if (hash) {
return g_hash_table_lookup(hash, key);
}
return NULL;
}
extern action_t *get_pseudo_op(const char *name, pe_working_set_t * data_set);
extern gboolean order_actions(action_t * lh_action, action_t * rh_action, enum pe_ordering order);
extern GListPtr node_list_and(GListPtr list1, GListPtr list2, gboolean filter);
extern GListPtr node_list_xor(GListPtr list1, GListPtr list2, gboolean filter);
extern GListPtr node_list_minus(GListPtr list1, GListPtr list2, gboolean filter);
extern void pe_free_shallow(GListPtr alist);
extern void pe_free_shallow_adv(GListPtr alist, gboolean with_data);
/* For creating the transition graph */
extern xmlNode *action2xml(action_t * action, gboolean as_input);
/* Printing functions for debug */
extern void print_node(const char *pre_text, node_t * node, gboolean details);
extern void print_resource(int log_level, const char *pre_text, resource_t * rsc, gboolean details);
extern void dump_node_scores_worker(int level, const char *file, const char *function, int line,
resource_t * rsc, const char *comment, GHashTable * nodes);
extern void dump_node_capacity(int level, const char *comment, node_t * node);
extern void dump_rsc_utilization(int level, const char *comment, resource_t * rsc, node_t * node);
# define dump_node_scores(level, rsc, text, nodes) do { \
dump_node_scores_worker(level, __FILE__, __FUNCTION__, __LINE__, rsc, text, nodes); \
} while(0)
/* Sorting functions */
extern gint sort_rsc_priority(gconstpointer a, gconstpointer b);
extern gint sort_rsc_index(gconstpointer a, gconstpointer b);
extern xmlNode *find_rsc_op_entry(resource_t * rsc, const char *key);
extern action_t *custom_action(resource_t * rsc, char *key, const char *task, node_t * on_node,
gboolean optional, gboolean foo, pe_working_set_t * data_set);
# define delete_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_DELETE, 0)
# define delete_action(rsc, node, optional) custom_action( \
rsc, delete_key(rsc), CRMD_ACTION_DELETE, node, \
optional, TRUE, data_set);
# define stopped_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_STOPPED, 0)
# define stopped_action(rsc, node, optional) custom_action( \
rsc, stopped_key(rsc), CRMD_ACTION_STOPPED, node, \
optional, TRUE, data_set);
# define stop_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_STOP, 0)
# define stop_action(rsc, node, optional) custom_action( \
rsc, stop_key(rsc), CRMD_ACTION_STOP, node, \
optional, TRUE, data_set);
# define start_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_START, 0)
# define start_action(rsc, node, optional) custom_action( \
rsc, start_key(rsc), CRMD_ACTION_START, node, \
optional, TRUE, data_set)
# define started_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_STARTED, 0)
# define started_action(rsc, node, optional) custom_action( \
rsc, started_key(rsc), CRMD_ACTION_STARTED, node, \
optional, TRUE, data_set)
# define promote_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_PROMOTE, 0)
# define promote_action(rsc, node, optional) custom_action( \
rsc, promote_key(rsc), CRMD_ACTION_PROMOTE, node, \
optional, TRUE, data_set)
# define promoted_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_PROMOTED, 0)
# define promoted_action(rsc, node, optional) custom_action( \
rsc, promoted_key(rsc), CRMD_ACTION_PROMOTED, node, \
optional, TRUE, data_set)
# define demote_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_DEMOTE, 0)
# define demote_action(rsc, node, optional) custom_action( \
rsc, demote_key(rsc), CRMD_ACTION_DEMOTE, node, \
optional, TRUE, data_set)
# define demoted_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_DEMOTED, 0)
# define demoted_action(rsc, node, optional) custom_action( \
rsc, demoted_key(rsc), CRMD_ACTION_DEMOTED, node, \
optional, TRUE, data_set)
extern action_t *find_first_action(GListPtr input, const char *uuid, const char *task,
node_t * on_node);
extern enum action_tasks get_complex_task(resource_t * rsc, const char *name,
gboolean allow_non_atomic);
extern GListPtr find_actions(GListPtr input, const char *key, node_t * on_node);
extern GListPtr find_actions_exact(GListPtr input, const char *key, node_t * on_node);
extern GListPtr find_recurring_actions(GListPtr input, node_t * not_on_node);
extern void pe_free_action(action_t * action);
extern void
resource_location(resource_t * rsc, node_t * node, int score, const char *tag,
pe_working_set_t * data_set);
extern gint sort_op_by_callid(gconstpointer a, gconstpointer b);
extern gboolean get_target_role(resource_t * rsc, enum rsc_role_e *role);
extern resource_t *find_clone_instance(resource_t * rsc, const char *sub_id,
pe_working_set_t * data_set);
+extern void destroy_ticket(gpointer data);
+extern ticket_t *ticket_new(const char * ticket_id, pe_working_set_t * data_set);
+
#endif
diff --git a/pengine/constraints.c b/pengine/constraints.c
index 44da77a122..8a7d9db821 100644
--- a/pengine/constraints.c
+++ b/pengine/constraints.c
@@ -1,2345 +1,2338 @@
/*
* 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 <crm/common/msg.h>
#include <glib.h>
#include <crm/pengine/status.h>
#include <pengine.h>
#include <allocate.h>
#include <utils.h>
#include <crm/pengine/rules.h>
#include <lib/pengine/utils.h>
enum pe_order_kind {
pe_order_kind_optional,
pe_order_kind_mandatory,
pe_order_kind_serialize,
};
#define EXPAND_CONSTRAINT_IDREF(__set, __rsc, __name) do { \
__rsc = pe_find_resource(data_set->resources, __name); \
if(__rsc == NULL) { \
crm_config_err("%s: No resource found for %s", __set, __name); \
return FALSE; \
} \
} while(0)
enum pe_ordering get_flags(const char *id, enum pe_order_kind kind,
const char *action_first, const char *action_then, gboolean invert);
enum pe_ordering get_asymmetrical_flags(enum pe_order_kind kind);
gboolean
unpack_constraints(xmlNode * xml_constraints, pe_working_set_t * data_set)
{
xmlNode *xml_obj = NULL;
xmlNode *lifetime = NULL;
for (xml_obj = __xml_first_child(xml_constraints); xml_obj != NULL;
xml_obj = __xml_next(xml_obj)) {
const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
if (id == NULL) {
crm_config_err("Constraint <%s...> must have an id", crm_element_name(xml_obj));
continue;
}
crm_trace("Processing constraint %s %s", crm_element_name(xml_obj), id);
lifetime = first_named_child(xml_obj, "lifetime");
if (lifetime) {
crm_config_warn("Support for the lifetime tag, used by %s, is deprecated."
" The rules it contains should instead be direct decendants of the constraint object",
id);
}
if (test_ruleset(lifetime, NULL, data_set->now) == FALSE) {
crm_info("Constraint %s %s is not active", crm_element_name(xml_obj), id);
} else if (safe_str_eq(XML_CONS_TAG_RSC_ORDER, crm_element_name(xml_obj))) {
unpack_rsc_order(xml_obj, data_set);
} else if (safe_str_eq(XML_CONS_TAG_RSC_DEPEND, crm_element_name(xml_obj))) {
unpack_rsc_colocation(xml_obj, data_set);
} else if (safe_str_eq(XML_CONS_TAG_RSC_LOCATION, crm_element_name(xml_obj))) {
unpack_rsc_location(xml_obj, data_set);
} else if (safe_str_eq(XML_CONS_TAG_RSC_TICKET, crm_element_name(xml_obj))) {
unpack_rsc_ticket(xml_obj, data_set);
} else {
pe_err("Unsupported constraint type: %s", crm_element_name(xml_obj));
}
}
return TRUE;
}
static const char *
invert_action(const char *action)
{
if (safe_str_eq(action, RSC_START)) {
return RSC_STOP;
} else if (safe_str_eq(action, RSC_STOP)) {
return RSC_START;
} else if (safe_str_eq(action, RSC_PROMOTE)) {
return RSC_DEMOTE;
} else if (safe_str_eq(action, RSC_DEMOTE)) {
return RSC_PROMOTE;
} else if (safe_str_eq(action, RSC_PROMOTED)) {
return RSC_DEMOTED;
} else if (safe_str_eq(action, RSC_DEMOTED)) {
return RSC_PROMOTED;
} else if (safe_str_eq(action, RSC_STARTED)) {
return RSC_STOPPED;
} else if (safe_str_eq(action, RSC_STOPPED)) {
return RSC_STARTED;
}
crm_config_warn("Unknown action: %s", action);
return NULL;
}
static enum pe_order_kind
get_ordering_type(xmlNode * xml_obj)
{
enum pe_order_kind kind_e = pe_order_kind_mandatory;
const char *kind = crm_element_value(xml_obj, XML_ORDER_ATTR_KIND);
if (kind == NULL) {
const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
kind_e = pe_order_kind_mandatory;
if (score) {
int score_i = char2score(score);
if (score_i == 0) {
kind_e = pe_order_kind_optional;
}
/* } else if(rsc_then->variant == pe_native && rsc_first->variant > pe_group) { */
/* kind_e = pe_order_kind_optional; */
}
} else if (safe_str_eq(kind, "Mandatory")) {
kind_e = pe_order_kind_mandatory;
} else if (safe_str_eq(kind, "Optional")) {
kind_e = pe_order_kind_optional;
} else if (safe_str_eq(kind, "Serialize")) {
kind_e = pe_order_kind_serialize;
} else {
const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
crm_config_err("Constraint %s: Unknown type '%s'", id, kind);
}
return kind_e;
}
static gboolean
contains_stonith(resource_t * rsc)
{
GListPtr gIter = rsc->children;
if (gIter == FALSE) {
const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
if (safe_str_eq(class, "stonith")) {
return TRUE;
}
}
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child = (resource_t *) gIter->data;
if (contains_stonith(child)) {
return TRUE;
}
}
return FALSE;
}
static gboolean
unpack_simple_rsc_order(xmlNode * xml_obj, pe_working_set_t * data_set)
{
int order_id = 0;
resource_t *rsc_then = NULL;
resource_t *rsc_first = NULL;
gboolean invert_bool = TRUE;
enum pe_order_kind kind = pe_order_kind_mandatory;
enum pe_ordering cons_weight = pe_order_optional;
const char *id_first = NULL;
const char *id_then = NULL;
const char *action_then = NULL;
const char *action_first = NULL;
const char *instance_then = NULL;
const char *instance_first = NULL;
const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
const char *invert = crm_element_value(xml_obj, XML_CONS_ATTR_SYMMETRICAL);
crm_str_to_boolean(invert, &invert_bool);
if (xml_obj == NULL) {
crm_config_err("No constraint object to process.");
return FALSE;
} else if (id == NULL) {
crm_config_err("%s constraint must have an id", crm_element_name(xml_obj));
return FALSE;
}
id_then = crm_element_value(xml_obj, XML_ORDER_ATTR_THEN);
id_first = crm_element_value(xml_obj, XML_ORDER_ATTR_FIRST);
action_then = crm_element_value(xml_obj, XML_ORDER_ATTR_THEN_ACTION);
action_first = crm_element_value(xml_obj, XML_ORDER_ATTR_FIRST_ACTION);
instance_then = crm_element_value(xml_obj, XML_ORDER_ATTR_THEN_INSTANCE);
instance_first = crm_element_value(xml_obj, XML_ORDER_ATTR_FIRST_INSTANCE);
if (action_first == NULL) {
action_first = RSC_START;
}
if (action_then == NULL) {
action_then = action_first;
}
if (id_then == NULL || id_first == NULL) {
crm_config_err("Constraint %s needs two sides lh: %s rh: %s",
id, crm_str(id_then), crm_str(id_first));
return FALSE;
}
rsc_then = pe_find_resource(data_set->resources, id_then);
rsc_first = pe_find_resource(data_set->resources, id_first);
if (rsc_then == NULL) {
crm_config_err("Constraint %s: no resource found for name '%s'", id, id_then);
return FALSE;
} else if (rsc_first == NULL) {
crm_config_err("Constraint %s: no resource found for name '%s'", id, id_first);
return FALSE;
} else if (instance_then && rsc_then->variant < pe_clone) {
crm_config_err("Invalid constraint '%s':"
" Resource '%s' is not a clone but instance %s was requested",
id, id_then, instance_then);
return FALSE;
} else if (instance_first && rsc_first->variant < pe_clone) {
crm_config_err("Invalid constraint '%s':"
" Resource '%s' is not a clone but instance %s was requested",
id, id_first, instance_first);
return FALSE;
}
if (instance_then) {
rsc_then = find_clone_instance(rsc_then, instance_then, data_set);
if (rsc_then == NULL) {
crm_config_warn("Invalid constraint '%s': No instance '%s' of '%s'", id, instance_then,
id_then);
return FALSE;
}
}
if (instance_first) {
rsc_first = find_clone_instance(rsc_first, instance_first, data_set);
if (rsc_first == NULL) {
crm_config_warn("Invalid constraint '%s': No instance '%s' of '%s'", id, instance_first,
id_first);
return FALSE;
}
}
if (safe_str_eq(action_first, RSC_STOP) && contains_stonith(rsc_then)) {
if (contains_stonith(rsc_first) == FALSE) {
crm_config_err
("Constraint %s: Ordering STONITH resource (%s) to stop before %s is illegal", id,
rsc_first->id, rsc_then->id);
}
return FALSE;
}
cons_weight = pe_order_optional;
kind = get_ordering_type(xml_obj);
if (kind == pe_order_kind_optional && rsc_then->restart_type == pe_restart_restart) {
crm_trace("Upgrade : recovery - implies right");
cons_weight |= pe_order_implies_then;
}
if (invert_bool == FALSE) {
cons_weight |= get_asymmetrical_flags(kind);
} else {
cons_weight |= get_flags(id, kind, action_first, action_then, FALSE);
}
order_id = new_rsc_order(rsc_first, action_first, rsc_then, action_then, cons_weight, data_set);
crm_trace("order-%d (%s): %s_%s before %s_%s flags=0x%.6x",
order_id, id, rsc_first->id, action_first, rsc_then->id, action_then, cons_weight);
if (invert_bool == FALSE) {
return TRUE;
} else if (invert && kind == pe_order_kind_serialize) {
crm_config_warn("Cannot invert serialized constraint set %s", id);
return TRUE;
} else if (kind == pe_order_kind_serialize) {
return TRUE;
}
action_then = invert_action(action_then);
action_first = invert_action(action_first);
if (action_then == NULL || action_first == NULL) {
crm_config_err("Cannot invert rsc_order constraint %s."
" Please specify the inverse manually.", id);
return TRUE;
}
if (safe_str_eq(action_first, RSC_STOP) && contains_stonith(rsc_then)) {
if (contains_stonith(rsc_first) == FALSE) {
crm_config_err
("Constraint %s: Ordering STONITH resource (%s) to stop before %s is illegal", id,
rsc_first->id, rsc_then->id);
}
return FALSE;
}
cons_weight = pe_order_optional;
if (kind == pe_order_kind_optional && rsc_then->restart_type == pe_restart_restart) {
crm_trace("Upgrade : recovery - implies left");
cons_weight |= pe_order_implies_first;
}
cons_weight |= get_flags(id, kind, action_first, action_then, TRUE);
order_id = new_rsc_order(rsc_then, action_then, rsc_first, action_first, cons_weight, data_set);
crm_trace("order-%d (%s): %s_%s before %s_%s flags=0x%.6x",
order_id, id, rsc_then->id, action_then, rsc_first->id, action_first, cons_weight);
return TRUE;
}
gboolean
unpack_rsc_location(xmlNode * xml_obj, pe_working_set_t * data_set)
{
gboolean empty = TRUE;
rsc_to_node_t *location = NULL;
const char *id_lh = crm_element_value(xml_obj, "rsc");
const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
resource_t *rsc_lh = pe_find_resource(data_set->resources, id_lh);
const char *node = crm_element_value(xml_obj, "node");
const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
const char *domain = crm_element_value(xml_obj, XML_CIB_TAG_DOMAIN);
const char *role = crm_element_value(xml_obj, XML_RULE_ATTR_ROLE);
if (rsc_lh == NULL) {
/* only a warn as BSC adds the constraint then the resource */
crm_config_warn("No resource (con=%s, rsc=%s)", id, id_lh);
return FALSE;
}
if (domain) {
GListPtr nodes = g_hash_table_lookup(data_set->domains, domain);
if (domain == NULL) {
crm_config_err("Invalid constraint %s: Domain %s does not exist", id, domain);
return FALSE;
}
location = rsc2node_new(id, rsc_lh, 0, NULL, data_set);
location->node_list_rh = node_list_dup(nodes, FALSE, FALSE);
} else if (node != NULL && score != NULL) {
int score_i = char2score(score);
node_t *match = pe_find_node(data_set->nodes, node);
if (!match) {
return FALSE;
}
location = rsc2node_new(id, rsc_lh, score_i, match, data_set);
} else {
xmlNode *rule_xml = NULL;
for (rule_xml = __xml_first_child(xml_obj); rule_xml != NULL;
rule_xml = __xml_next(rule_xml)) {
if (crm_str_eq((const char *)rule_xml->name, XML_TAG_RULE, TRUE)) {
empty = FALSE;
crm_trace("Unpacking %s/%s", id, ID(rule_xml));
generate_location_rule(rsc_lh, rule_xml, data_set);
}
}
if (empty) {
crm_config_err("Invalid location constraint %s:"
" rsc_location must contain at least one rule", ID(xml_obj));
}
}
if (location && role) {
if (text2role(role) == RSC_ROLE_UNKNOWN) {
pe_err("Invalid constraint %s: Bad role %s", id, role);
return FALSE;
} else {
location->role_filter = text2role(role);
if (location->role_filter == RSC_ROLE_SLAVE) {
/* Fold slave back into Started for simplicity
* At the point Slave location constraints are evaluated,
* all resources are still either stopped or started
*/
location->role_filter = RSC_ROLE_STARTED;
}
}
}
return TRUE;
}
static int
get_node_score(const char *rule, const char *score, gboolean raw, node_t * node)
{
int score_f = 0;
if (score == NULL) {
pe_err("Rule %s: no score specified. Assuming 0.", rule);
} else if (raw) {
score_f = char2score(score);
} else {
const char *attr_score = g_hash_table_lookup(node->details->attrs, score);
if (attr_score == NULL) {
crm_debug("Rule %s: node %s did not have a value for %s",
rule, node->details->uname, score);
score_f = -INFINITY;
} else {
crm_debug("Rule %s: node %s had value %s for %s",
rule, node->details->uname, attr_score, score);
score_f = char2score(attr_score);
}
}
return score_f;
}
rsc_to_node_t *
generate_location_rule(resource_t * rsc, xmlNode * rule_xml, pe_working_set_t * data_set)
{
const char *rule_id = NULL;
const char *score = NULL;
const char *boolean = NULL;
const char *role = NULL;
GListPtr gIter = NULL;
GListPtr match_L = NULL;
int score_f = 0;
gboolean do_and = TRUE;
gboolean accept = TRUE;
gboolean raw_score = TRUE;
rsc_to_node_t *location_rule = NULL;
rule_xml = expand_idref(rule_xml, data_set->input);
rule_id = crm_element_value(rule_xml, XML_ATTR_ID);
boolean = crm_element_value(rule_xml, XML_RULE_ATTR_BOOLEAN_OP);
role = crm_element_value(rule_xml, XML_RULE_ATTR_ROLE);
crm_trace("Processing rule: %s", rule_id);
if (role != NULL && text2role(role) == RSC_ROLE_UNKNOWN) {
pe_err("Bad role specified for %s: %s", rule_id, role);
return NULL;
}
score = crm_element_value(rule_xml, XML_RULE_ATTR_SCORE);
if (score != NULL) {
score_f = char2score(score);
} else {
score = crm_element_value(rule_xml, XML_RULE_ATTR_SCORE_ATTRIBUTE);
if (score == NULL) {
score = crm_element_value(rule_xml, XML_RULE_ATTR_SCORE_MANGLED);
}
if (score != NULL) {
raw_score = FALSE;
}
}
if (safe_str_eq(boolean, "or")) {
do_and = FALSE;
}
location_rule = rsc2node_new(rule_id, rsc, 0, NULL, data_set);
if (location_rule == NULL) {
return NULL;
}
if (role != NULL) {
crm_trace("Setting role filter: %s", role);
location_rule->role_filter = text2role(role);
if (location_rule->role_filter == RSC_ROLE_SLAVE) {
/* Fold slave back into Started for simplicity
* At the point Slave location constraints are evaluated,
* all resources are still either stopped or started
*/
location_rule->role_filter = RSC_ROLE_STARTED;
}
}
if (do_and) {
GListPtr gIter = NULL;
match_L = node_list_dup(data_set->nodes, TRUE, FALSE);
for (gIter = match_L; gIter != NULL; gIter = gIter->next) {
node_t *node = (node_t *) gIter->data;
node->weight = get_node_score(rule_id, score, raw_score, node);
}
}
for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
node_t *node = (node_t *) gIter->data;
accept = test_rule(rule_xml, node->details->attrs, RSC_ROLE_UNKNOWN, data_set->now);
crm_trace("Rule %s %s on %s", ID(rule_xml), accept ? "passed" : "failed",
node->details->uname);
score_f = get_node_score(rule_id, score, raw_score, node);
/* if(accept && score_f == -INFINITY) { */
/* accept = FALSE; */
/* } */
if (accept) {
node_t *local = pe_find_node_id(match_L, node->details->id);
if (local == NULL && do_and) {
continue;
} else if (local == NULL) {
local = node_copy(node);
match_L = g_list_append(match_L, local);
}
if (do_and == FALSE) {
local->weight = merge_weights(local->weight, score_f);
}
crm_trace("node %s now has weight %d", node->details->uname, local->weight);
} else if (do_and && !accept) {
/* remove it */
node_t *delete = pe_find_node_id(match_L, node->details->id);
if (delete != NULL) {
match_L = g_list_remove(match_L, delete);
crm_trace("node %s did not match", node->details->uname);
}
crm_free(delete);
}
}
location_rule->node_list_rh = match_L;
if (location_rule->node_list_rh == NULL) {
crm_trace("No matching nodes for rule %s", rule_id);
return NULL;
}
crm_trace("%s: %d nodes matched", rule_id, g_list_length(location_rule->node_list_rh));
return location_rule;
}
static gint
sort_cons_priority_lh(gconstpointer a, gconstpointer b)
{
const rsc_colocation_t *rsc_constraint1 = (const rsc_colocation_t *)a;
const rsc_colocation_t *rsc_constraint2 = (const rsc_colocation_t *)b;
if (a == NULL) {
return 1;
}
if (b == NULL) {
return -1;
}
CRM_ASSERT(rsc_constraint1->rsc_lh != NULL);
CRM_ASSERT(rsc_constraint1->rsc_rh != NULL);
if (rsc_constraint1->rsc_lh->priority > rsc_constraint2->rsc_lh->priority) {
return -1;
}
if (rsc_constraint1->rsc_lh->priority < rsc_constraint2->rsc_lh->priority) {
return 1;
}
return strcmp(rsc_constraint1->rsc_lh->id, rsc_constraint2->rsc_lh->id);
}
static gint
sort_cons_priority_rh(gconstpointer a, gconstpointer b)
{
const rsc_colocation_t *rsc_constraint1 = (const rsc_colocation_t *)a;
const rsc_colocation_t *rsc_constraint2 = (const rsc_colocation_t *)b;
if (a == NULL) {
return 1;
}
if (b == NULL) {
return -1;
}
CRM_ASSERT(rsc_constraint1->rsc_lh != NULL);
CRM_ASSERT(rsc_constraint1->rsc_rh != NULL);
if (rsc_constraint1->rsc_rh->priority > rsc_constraint2->rsc_rh->priority) {
return -1;
}
if (rsc_constraint1->rsc_rh->priority < rsc_constraint2->rsc_rh->priority) {
return 1;
}
return strcmp(rsc_constraint1->rsc_rh->id, rsc_constraint2->rsc_rh->id);
}
gboolean
rsc_colocation_new(const char *id, const char *node_attr, int score,
resource_t * rsc_lh, resource_t * rsc_rh,
const char *state_lh, const char *state_rh, pe_working_set_t * data_set)
{
rsc_colocation_t *new_con = NULL;
if (rsc_lh == NULL) {
crm_config_err("No resource found for LHS %s", id);
return FALSE;
} else if (rsc_rh == NULL) {
crm_config_err("No resource found for RHS of %s", id);
return FALSE;
}
crm_malloc0(new_con, sizeof(rsc_colocation_t));
if (new_con == NULL) {
return FALSE;
}
if (state_lh == NULL || safe_str_eq(state_lh, RSC_ROLE_STARTED_S)) {
state_lh = RSC_ROLE_UNKNOWN_S;
}
if (state_rh == NULL || safe_str_eq(state_rh, RSC_ROLE_STARTED_S)) {
state_rh = RSC_ROLE_UNKNOWN_S;
}
new_con->id = id;
new_con->rsc_lh = rsc_lh;
new_con->rsc_rh = rsc_rh;
new_con->score = score;
new_con->role_lh = text2role(state_lh);
new_con->role_rh = text2role(state_rh);
new_con->node_attribute = node_attr;
if (node_attr == NULL) {
node_attr = "#" XML_ATTR_UNAME;
}
crm_trace("%s ==> %s (%s %d)", rsc_lh->id, rsc_rh->id, node_attr, score);
rsc_lh->rsc_cons = g_list_insert_sorted(rsc_lh->rsc_cons, new_con, sort_cons_priority_rh);
rsc_rh->rsc_cons_lhs =
g_list_insert_sorted(rsc_rh->rsc_cons_lhs, new_con, sort_cons_priority_lh);
data_set->colocation_constraints = g_list_append(data_set->colocation_constraints, new_con);
return TRUE;
}
/* LHS before RHS */
int
new_rsc_order(resource_t * lh_rsc, const char *lh_task,
resource_t * rh_rsc, const char *rh_task,
enum pe_ordering type, pe_working_set_t * data_set)
{
char *lh_key = NULL;
char *rh_key = NULL;
CRM_CHECK(lh_rsc != NULL, return -1);
CRM_CHECK(lh_task != NULL, return -1);
CRM_CHECK(rh_rsc != NULL, return -1);
CRM_CHECK(rh_task != NULL, return -1);
lh_key = generate_op_key(lh_rsc->id, lh_task, 0);
rh_key = generate_op_key(rh_rsc->id, rh_task, 0);
return custom_action_order(lh_rsc, lh_key, NULL, rh_rsc, rh_key, NULL, type, data_set);
}
/* LHS before RHS */
int
custom_action_order(resource_t * lh_rsc, char *lh_action_task, action_t * lh_action,
resource_t * rh_rsc, char *rh_action_task, action_t * rh_action,
enum pe_ordering type, pe_working_set_t * data_set)
{
order_constraint_t *order = NULL;
if (lh_rsc == NULL && lh_action) {
lh_rsc = lh_action->rsc;
}
if (rh_rsc == NULL && rh_action) {
rh_rsc = rh_action->rsc;
}
if ((lh_action == NULL && lh_rsc == NULL)
|| (rh_action == NULL && rh_rsc == NULL)) {
crm_config_err("Invalid inputs %p.%p %p.%p", lh_rsc, lh_action, rh_rsc, rh_action);
crm_free(lh_action_task);
crm_free(rh_action_task);
return -1;
}
crm_malloc0(order, sizeof(order_constraint_t));
order->id = data_set->order_id++;
order->type = type;
order->lh_rsc = lh_rsc;
order->rh_rsc = rh_rsc;
order->lh_action = lh_action;
order->rh_action = rh_action;
order->lh_action_task = lh_action_task;
order->rh_action_task = rh_action_task;
if (order->lh_action_task == NULL && lh_action) {
order->lh_action_task = crm_strdup(lh_action->uuid);
}
if (order->rh_action_task == NULL && rh_action) {
order->rh_action_task = crm_strdup(rh_action->uuid);
}
if (order->lh_rsc == NULL && lh_action) {
order->lh_rsc = lh_action->rsc;
}
if (order->rh_rsc == NULL && rh_action) {
order->rh_rsc = rh_action->rsc;
}
data_set->ordering_constraints = g_list_prepend(data_set->ordering_constraints, order);
return order->id;
}
enum pe_ordering
get_asymmetrical_flags(enum pe_order_kind kind)
{
enum pe_ordering flags = pe_order_optional;
if (kind == pe_order_kind_mandatory) {
flags |= pe_order_asymmetrical;
} else if (kind == pe_order_kind_serialize) {
flags |= pe_order_serialize_only;
}
return flags;
}
enum pe_ordering
get_flags(const char *id, enum pe_order_kind kind,
const char *action_first, const char *action_then, gboolean invert)
{
enum pe_ordering flags = pe_order_optional;
if (invert && kind == pe_order_kind_mandatory) {
crm_trace("Upgrade %s: implies left", id);
flags |= pe_order_implies_first;
} else if (kind == pe_order_kind_mandatory) {
crm_trace("Upgrade %s: implies right", id);
flags |= pe_order_implies_then;
if (safe_str_eq(action_first, RSC_START)
|| safe_str_eq(action_first, RSC_PROMOTE)) {
crm_trace("Upgrade %s: runnable", id);
flags |= pe_order_runnable_left;
}
} else if (kind == pe_order_kind_serialize) {
flags |= pe_order_serialize_only;
}
return flags;
}
static gboolean
unpack_order_set(xmlNode * set, enum pe_order_kind kind, resource_t ** rsc,
action_t ** begin, action_t ** end, action_t ** inv_begin, action_t ** inv_end,
const char *symmetrical, pe_working_set_t * data_set)
{
xmlNode *xml_rsc = NULL;
GListPtr set_iter = NULL;
GListPtr resources = NULL;
resource_t *last = NULL;
resource_t *resource = NULL;
int local_kind = kind;
gboolean sequential = FALSE;
enum pe_ordering flags = pe_order_optional;
char *key = NULL;
const char *id = ID(set);
const char *action = crm_element_value(set, "action");
const char *sequential_s = crm_element_value(set, "sequential");
const char *kind_s = crm_element_value(set, XML_ORDER_ATTR_KIND);
/*
char *pseudo_id = NULL;
char *end_id = NULL;
char *begin_id = NULL;
*/
if (action == NULL) {
action = RSC_START;
}
if (kind_s) {
local_kind = get_ordering_type(set);
}
if (sequential_s == NULL) {
sequential_s = "1";
}
sequential = crm_is_true(sequential_s);
if (crm_is_true(symmetrical)) {
flags = get_flags(id, local_kind, action, action, FALSE);
} else {
flags = get_asymmetrical_flags(local_kind);
}
for (xml_rsc = __xml_first_child(set); xml_rsc != NULL; xml_rsc = __xml_next(xml_rsc)) {
if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
EXPAND_CONSTRAINT_IDREF(id, resource, ID(xml_rsc));
resources = g_list_append(resources, resource);
}
}
if (g_list_length(resources) == 1) {
crm_trace("Single set: %s", id);
*rsc = resource;
*end = NULL;
*begin = NULL;
*inv_end = NULL;
*inv_begin = NULL;
goto done;
}
/*
pseudo_id = crm_concat(id, action, '-');
end_id = crm_concat(pseudo_id, "end", '-');
begin_id = crm_concat(pseudo_id, "begin", '-');
*/
*rsc = NULL;
/*
*end = get_pseudo_op(end_id, data_set);
*begin = get_pseudo_op(begin_id, data_set);
crm_free(pseudo_id);
crm_free(begin_id);
crm_free(end_id);
*/
set_iter = resources;
while (set_iter != NULL) {
resource = (resource_t *) set_iter->data;
set_iter = set_iter->next;
key = generate_op_key(resource->id, action, 0);
/*
custom_action_order(NULL, NULL, *begin, resource, crm_strdup(key), NULL,
flags|pe_order_implies_first_printed, data_set);
custom_action_order(resource, crm_strdup(key), NULL, NULL, NULL, *end,
flags|pe_order_implies_then_printed, data_set);
*/
if (local_kind == pe_order_kind_serialize) {
/* Serialize before everything that comes after */
GListPtr gIter = NULL;
for (gIter = set_iter; gIter != NULL; gIter = gIter->next) {
resource_t *then_rsc = (resource_t *) gIter->data;
char *then_key = generate_op_key(then_rsc->id, action, 0);
custom_action_order(resource, crm_strdup(key), NULL, then_rsc, then_key, NULL,
flags, data_set);
}
} else if (sequential) {
if (last != NULL) {
new_rsc_order(last, action, resource, action, flags, data_set);
}
last = resource;
}
crm_free(key);
}
if (crm_is_true(symmetrical) == FALSE) {
goto done;
} else if (symmetrical && local_kind == pe_order_kind_serialize) {
crm_config_warn("Cannot invert serialized constraint set %s", id);
goto done;
} else if (local_kind == pe_order_kind_serialize) {
goto done;
}
last = NULL;
action = invert_action(action);
/*
pseudo_id = crm_concat(id, action, '-');
end_id = crm_concat(pseudo_id, "end", '-');
begin_id = crm_concat(pseudo_id, "begin", '-');
*inv_end = get_pseudo_op(end_id, data_set);
*inv_begin = get_pseudo_op(begin_id, data_set);
crm_free(pseudo_id);
crm_free(begin_id);
crm_free(end_id);
*/
flags = get_flags(id, local_kind, action, action, TRUE);
set_iter = resources;
while (set_iter != NULL) {
resource = (resource_t *) set_iter->data;
set_iter = set_iter->next;
/*
key = generate_op_key(resource->id, action, 0);
custom_action_order(NULL, NULL, *inv_begin, resource, crm_strdup(key), NULL,
flags|pe_order_implies_first_printed, data_set);
custom_action_order(resource, key, NULL, NULL, NULL, *inv_end,
flags|pe_order_implies_then_printed, data_set);
*/
if (sequential) {
if (last != NULL) {
new_rsc_order(resource, action, last, action, flags, data_set);
}
last = resource;
}
}
done:
g_list_free(resources);
return TRUE;
}
static gboolean
order_rsc_sets(const char *id, xmlNode * set1, xmlNode * set2, enum pe_order_kind kind,
pe_working_set_t * data_set, gboolean invert, gboolean symmetrical)
{
xmlNode *xml_rsc = NULL;
resource_t *rsc_1 = NULL;
resource_t *rsc_2 = NULL;
const char *action_1 = crm_element_value(set1, "action");
const char *action_2 = crm_element_value(set2, "action");
const char *sequential_1 = crm_element_value(set1, "sequential");
const char *sequential_2 = crm_element_value(set2, "sequential");
const char *require_all_s = crm_element_value(set1, "require-all");
gboolean require_all = require_all_s ? crm_is_true(require_all_s) : TRUE;
enum pe_ordering flags = pe_order_none;
if (action_1 == NULL) {
action_1 = RSC_START;
};
if (action_2 == NULL) {
action_2 = RSC_START;
};
if (invert) {
action_1 = invert_action(action_1);
action_2 = invert_action(action_2);
}
if (symmetrical == FALSE) {
flags = get_asymmetrical_flags(kind);
} else {
flags = get_flags(id, kind, action_2, action_1, invert);
}
/* If we have an un-ordered set1, whether it is sequential or not is irrelevant in regards to set2. */
if (!require_all) {
action_t *unordered_action = get_pseudo_op(crm_concat(CRM_OP_RELAXED_SET, ID(set1), ':'), data_set);
update_action_flags(unordered_action, pe_action_requires_any);
for (xml_rsc = __xml_first_child(set1); xml_rsc != NULL; xml_rsc = __xml_next(xml_rsc)) {
xmlNode *xml_rsc_2 = NULL;
if (!crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
continue;
}
EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
/* Add an ordering constraint between every element in set1 and the pseudo action.
* If any action in set1 is runnable the pseudo action will be runnable. */
custom_action_order(rsc_1, generate_op_key(rsc_1->id, action_1, 0), NULL,
NULL,NULL, unordered_action,
pe_order_one_or_more|pe_order_implies_then_printed, data_set);
for (xml_rsc_2 = __xml_first_child(set2); xml_rsc_2 != NULL; xml_rsc_2 = __xml_next(xml_rsc_2)) {
if (!crm_str_eq((const char *)xml_rsc_2->name, XML_TAG_RESOURCE_REF, TRUE)) {
continue;
}
EXPAND_CONSTRAINT_IDREF(id, rsc_2, ID(xml_rsc_2));
/* Add an ordering constraint between the pseudo action and every element in set2.
* If the pseudo action is runnable, every action in set2 will be runnable */
custom_action_order(NULL, NULL, unordered_action,
rsc_2, generate_op_key(rsc_2->id, action_2, 0), NULL,
flags|pe_order_runnable_left, data_set);
}
}
return TRUE;
}
if (crm_is_true(sequential_1)) {
if (invert == FALSE) {
/* get the last one */
const char *rid = NULL;
for (xml_rsc = __xml_first_child(set1); xml_rsc != NULL; xml_rsc = __xml_next(xml_rsc)) {
if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
rid = ID(xml_rsc);
}
}
EXPAND_CONSTRAINT_IDREF(id, rsc_1, rid);
} else {
/* get the first one */
for (xml_rsc = __xml_first_child(set1); xml_rsc != NULL; xml_rsc = __xml_next(xml_rsc)) {
if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
break;
}
}
}
}
if (crm_is_true(sequential_2)) {
if (invert == FALSE) {
/* get the first one */
for (xml_rsc = __xml_first_child(set2); xml_rsc != NULL; xml_rsc = __xml_next(xml_rsc)) {
if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
EXPAND_CONSTRAINT_IDREF(id, rsc_2, ID(xml_rsc));
break;
}
}
} else {
/* get the last one */
const char *rid = NULL;
for (xml_rsc = __xml_first_child(set2); xml_rsc != NULL; xml_rsc = __xml_next(xml_rsc)) {
if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
rid = ID(xml_rsc);
}
}
EXPAND_CONSTRAINT_IDREF(id, rsc_2, rid);
}
}
if (rsc_1 != NULL && rsc_2 != NULL) {
new_rsc_order(rsc_1, action_1, rsc_2, action_2, flags, data_set);
} else if (rsc_1 != NULL) {
for (xml_rsc = __xml_first_child(set2); xml_rsc != NULL; xml_rsc = __xml_next(xml_rsc)) {
if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
EXPAND_CONSTRAINT_IDREF(id, rsc_2, ID(xml_rsc));
new_rsc_order(rsc_1, action_1, rsc_2, action_2, flags, data_set);
}
}
} else if (rsc_2 != NULL) {
xmlNode *xml_rsc = NULL;
for (xml_rsc = __xml_first_child(set1); xml_rsc != NULL; xml_rsc = __xml_next(xml_rsc)) {
if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
new_rsc_order(rsc_1, action_1, rsc_2, action_2, flags, data_set);
}
}
} else {
for (xml_rsc = __xml_first_child(set1); xml_rsc != NULL; xml_rsc = __xml_next(xml_rsc)) {
if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
xmlNode *xml_rsc_2 = NULL;
EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
for (xml_rsc_2 = __xml_first_child(set2); xml_rsc_2 != NULL;
xml_rsc_2 = __xml_next(xml_rsc_2)) {
if (crm_str_eq((const char *)xml_rsc_2->name, XML_TAG_RESOURCE_REF, TRUE)) {
EXPAND_CONSTRAINT_IDREF(id, rsc_2, ID(xml_rsc_2));
new_rsc_order(rsc_1, action_1, rsc_2, action_2, flags, data_set);
}
}
}
}
}
return TRUE;
}
static gboolean
expand_templates_in_sets(xmlNode *xml_obj, xmlNode **expanded_xml, pe_working_set_t *data_set)
{
xmlNode *new_xml = NULL;
xmlNode *set = NULL;
gboolean any_refs = FALSE;
*expanded_xml = NULL;
if(xml_obj == NULL) {
crm_config_err("No constraint object to process.");
return FALSE;
}
new_xml = copy_xml(xml_obj);
for (set = __xml_first_child(new_xml); set != NULL; set = __xml_next(set)) {
xmlNode *xml_rsc = NULL;
GListPtr template_refs = NULL;
GListPtr gIter = NULL;
if (safe_str_neq((const char *)set->name, XML_CONS_TAG_RSC_SET)) {
continue;
}
for (xml_rsc = __xml_first_child(set); xml_rsc != NULL; xml_rsc = __xml_next(xml_rsc)) {
xmlNode *template_rsc_set = NULL;
if (safe_str_neq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF)) {
continue;
}
template_rsc_set = g_hash_table_lookup(data_set->template_rsc_sets, ID(xml_rsc));
if (template_rsc_set) {
/* The resource_ref under the resource_set references a template */
xmlNode *rsc_ref = NULL;
xmlNode *new_rsc_ref = NULL;
xmlNode *last_ref = xml_rsc;
/* A sample:
Original XML:
<resource_set id="template1-order-0" sequential="true">
<resource_ref id="rsc1"/>
<resource_ref id="template1"/>
<resource_ref id="rsc4"/>
</resource_set>
Now we are appending rsc2 and rsc3 which are derived from template1 right after it:
<resource_set id="template1-order-0" sequential="true">
<resource_ref id="rsc1"/>
<resource_ref id="template1"/>
<resource_ref id="rsc2"/>
<resource_ref id="rsc3"/>
<resource_ref id="rsc4"/>
</resource_set>
*/
for (rsc_ref = __xml_first_child(template_rsc_set); rsc_ref != NULL; rsc_ref = __xml_next(rsc_ref)) {
new_rsc_ref = xmlDocCopyNode(rsc_ref, getDocPtr(set), 1);
xmlAddNextSibling(last_ref, new_rsc_ref);
last_ref = new_rsc_ref;
}
any_refs = TRUE;
/* Do not directly free '<resource_ref id="template1"/>'.
That would break the further __xml_next(xml_rsc)) and cause "Invalid read" seen by valgrind.
So just record it into a hash table for freeing it later.
*/
template_refs = g_list_append(template_refs, xml_rsc);
}
}
/* Now free '<resource_ref id="template1"/>', and finally get:
<resource_set id="template1-order-0" sequential="true">
<resource_ref id="rsc1"/>
<resource_ref id="rsc2"/>
<resource_ref id="rsc3"/>
<resource_ref id="rsc4"/>
</resource_set>
*/
for (gIter = template_refs; gIter != NULL; gIter = gIter->next) {
xmlNode *template_ref = gIter->data;
free_xml_from_parent(NULL, template_ref);
}
g_list_free(template_refs);
}
if (any_refs) {
*expanded_xml = new_xml;
} else {
free_xml(new_xml);
}
return TRUE;
}
static gboolean
template_to_set(xmlNode *xml_obj, xmlNode **rsc_set, const char *attr,
gboolean convert_rsc, pe_working_set_t *data_set)
{
const char *cons_id = NULL;
const char *id = NULL;
resource_t *rsc = NULL;
*rsc_set = NULL;
if(xml_obj == NULL) {
crm_config_err("No constraint object to process.");
return FALSE;
}
if(attr == NULL) {
crm_config_err("No attribute name to process.");
return FALSE;
}
cons_id = crm_element_value(xml_obj, XML_ATTR_ID);
if(cons_id == NULL) {
crm_config_err("%s constraint must have an id", crm_element_name(xml_obj));
return FALSE;
}
id = crm_element_value(xml_obj, attr);
if(id == NULL) {
return TRUE;
}
rsc = pe_find_resource(data_set->resources, id);
if(rsc == NULL) {
xmlNode *template_rsc_set = g_hash_table_lookup(data_set->template_rsc_sets, id);
if(template_rsc_set == NULL) {
crm_config_err("Invalid constraint '%s': No template named '%s'", cons_id, id);
return FALSE;
}
/* A template is referenced by the "attr" attribute (first, then, rsc or with-rsc).
Add the template's corresponding "resource_set" which contains the primitives derived
from it under the constraint. */
*rsc_set = add_node_copy(xml_obj, template_rsc_set);
/* Set sequential="false" for the resource_set */
crm_xml_add(*rsc_set, "sequential", XML_BOOLEAN_FALSE);
} else if(convert_rsc) {
/* Even a regular resource is referenced by "attr", convert it into a resource_set.
Because the other side of the constraint could be a template reference. */
xmlNode *rsc_ref = NULL;
*rsc_set = create_xml_node(xml_obj, XML_CONS_TAG_RSC_SET);
crm_xml_add(*rsc_set, XML_ATTR_ID, id);
rsc_ref = create_xml_node(*rsc_set, XML_TAG_RESOURCE_REF);
crm_xml_add(rsc_ref, XML_ATTR_ID, id);
} else {
return TRUE;
}
/* Remove the "attr" attribute referencing the template */
if(*rsc_set) {
xml_remove_prop(xml_obj, attr);
}
return TRUE;
}
static gboolean
unpack_order_template(xmlNode *xml_obj, xmlNode **expanded_xml, pe_working_set_t *data_set)
{
const char *id = NULL;
const char *id_first = NULL;
const char *id_then = NULL;
const char *action_first = NULL;
const char *action_then = NULL;
resource_t *rsc_first = NULL;
resource_t *rsc_then = NULL;
xmlNode *new_xml = NULL;
xmlNode *rsc_set_first = NULL;
xmlNode *rsc_set_then = NULL;
gboolean any_sets = FALSE;
*expanded_xml = NULL;
if(xml_obj == NULL) {
crm_config_err("No constraint object to process.");
return FALSE;
}
id = crm_element_value(xml_obj, XML_ATTR_ID);
if(id == NULL) {
crm_config_err("%s constraint must have an id", crm_element_name(xml_obj));
return FALSE;
}
/* Attempt to expand any template references in possible resource sets. */
expand_templates_in_sets(xml_obj, &new_xml, data_set);
if (new_xml) {
/* There are resource sets referencing templates. Return with the expanded XML. */
crm_log_xml_debug_3(new_xml, "Expanded rsc_order...");
*expanded_xml = new_xml;
return TRUE;
}
id_first = crm_element_value(xml_obj, XML_ORDER_ATTR_FIRST);
id_then = crm_element_value(xml_obj, XML_ORDER_ATTR_THEN);
if(id_first == NULL || id_then == NULL) {
return TRUE;
}
rsc_first = pe_find_resource(data_set->resources, id_first);
rsc_then = pe_find_resource(data_set->resources, id_then);
if(rsc_first && rsc_then) {
/* Neither side references any template. */
return TRUE;
}
if (rsc_first == NULL) {
xmlNode *template_rsc_set_first = NULL;
gboolean rc = g_hash_table_lookup_extended(data_set->template_rsc_sets, id_first,
NULL, (gpointer) &template_rsc_set_first);
if (rc == FALSE) {
crm_config_err("Invalid constraint '%s': No resource or template named '%s'", id, id_first);
return FALSE;
} else if (template_rsc_set_first == NULL) {
crm_config_warn("Constraint '%s': No resource is derived from template '%s'", id, id_first);
return FALSE;
}
}
if (rsc_then == NULL) {
xmlNode *template_rsc_set_then = NULL;
gboolean rc = g_hash_table_lookup_extended(data_set->template_rsc_sets, id_then,
NULL, (gpointer) &template_rsc_set_then);
if (rc == FALSE) {
crm_config_err("Invalid constraint '%s': No resource or template named '%s'", id, id_then);
return FALSE;
} else if (template_rsc_set_then == NULL) {
crm_config_warn("Constraint '%s': No resource is derived from template '%s'", id, id_then);
return FALSE;
}
}
action_first = crm_element_value(xml_obj, XML_ORDER_ATTR_FIRST_ACTION);
action_then = crm_element_value(xml_obj, XML_ORDER_ATTR_THEN_ACTION);
new_xml = copy_xml(xml_obj);
/* Convert the template reference in "first" into a resource_set under the order constraint. */
if(template_to_set(new_xml, &rsc_set_first, XML_ORDER_ATTR_FIRST,
TRUE, data_set) == FALSE) {
free_xml(new_xml);
return FALSE;
}
if(rsc_set_first) {
if(action_first) {
/* A "first-action" is specified.
Move it into the converted resource_set as an "action" attribute. */
crm_xml_add(rsc_set_first, "action", action_first);
xml_remove_prop(new_xml, XML_ORDER_ATTR_FIRST_ACTION);
}
any_sets = TRUE;
}
/* Convert the template reference in "then" into a resource_set under the order constraint. */
if(template_to_set(new_xml, &rsc_set_then, XML_ORDER_ATTR_THEN,
TRUE, data_set) == FALSE) {
free_xml(new_xml);
return FALSE;
}
if(rsc_set_then) {
if(action_then) {
/* A "then-action" is specified.
Move it into the converted resource_set as an "action" attribute. */
crm_xml_add(rsc_set_then, "action", action_then);
xml_remove_prop(new_xml, XML_ORDER_ATTR_THEN_ACTION);
}
any_sets = TRUE;
}
if(any_sets) {
crm_log_xml_trace(new_xml, "Expanded rsc_order...");
*expanded_xml = new_xml;
} else {
free_xml(new_xml);
}
return TRUE;
}
gboolean
unpack_rsc_order(xmlNode * xml_obj, pe_working_set_t * data_set)
{
gboolean any_sets = FALSE;
resource_t *rsc = NULL;
/*
resource_t *last_rsc = NULL;
*/
action_t *set_end = NULL;
action_t *set_begin = NULL;
action_t *set_inv_end = NULL;
action_t *set_inv_begin = NULL;
xmlNode *set = NULL;
xmlNode *last = NULL;
xmlNode *orig_xml = NULL;
xmlNode *expanded_xml = NULL;
/*
action_t *last_end = NULL;
action_t *last_begin = NULL;
action_t *last_inv_end = NULL;
action_t *last_inv_begin = NULL;
*/
const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
const char *invert = crm_element_value(xml_obj, XML_CONS_ATTR_SYMMETRICAL);
enum pe_order_kind kind = get_ordering_type(xml_obj);
gboolean invert_bool = TRUE;
gboolean rc = TRUE;
if (invert == NULL) {
invert = "true";
}
invert_bool = crm_is_true(invert);
rc = unpack_order_template(xml_obj, &expanded_xml, data_set);
if(expanded_xml) {
orig_xml = xml_obj;
xml_obj = expanded_xml;
} else if (rc == FALSE) {
return FALSE;
}
for (set = __xml_first_child(xml_obj); set != NULL; set = __xml_next(set)) {
if (crm_str_eq((const char *)set->name, XML_CONS_TAG_RSC_SET, TRUE)) {
any_sets = TRUE;
set = expand_idref(set, data_set->input);
if (unpack_order_set(set, kind, &rsc, &set_begin, &set_end,
&set_inv_begin, &set_inv_end, invert, data_set) == FALSE) {
return FALSE;
/* Expand orders in order_rsc_sets() instead of via pseudo actions. */
/*
} else if(last) {
const char *set_action = crm_element_value(set, "action");
const char *last_action = crm_element_value(last, "action");
enum pe_ordering flags = get_flags(id, kind, last_action, set_action, FALSE);
if(!set_action) { set_action = RSC_START; }
if(!last_action) { last_action = RSC_START; }
if(rsc == NULL && last_rsc == NULL) {
order_actions(last_end, set_begin, flags);
} else {
custom_action_order(
last_rsc, null_or_opkey(last_rsc, last_action), last_end,
rsc, null_or_opkey(rsc, set_action), set_begin,
flags, data_set);
}
if(crm_is_true(invert)) {
set_action = invert_action(set_action);
last_action = invert_action(last_action);
flags = get_flags(id, kind, last_action, set_action, TRUE);
if(rsc == NULL && last_rsc == NULL) {
order_actions(last_inv_begin, set_inv_end, flags);
} else {
custom_action_order(
last_rsc, null_or_opkey(last_rsc, last_action), last_inv_begin,
rsc, null_or_opkey(rsc, set_action), set_inv_end,
flags, data_set);
}
}
*/
} else if ( /* never called -- Now call it for supporting clones in resource sets */
last) {
if (order_rsc_sets(id, last, set, kind, data_set, FALSE, invert_bool) == FALSE) {
return FALSE;
}
if (invert_bool && order_rsc_sets(id, set, last, kind, data_set, TRUE, invert_bool) == FALSE) {
return FALSE;
}
}
last = set;
/*
last_rsc = rsc;
last_end = set_end;
last_begin = set_begin;
last_inv_end = set_inv_end;
last_inv_begin = set_inv_begin;
*/
}
}
if(expanded_xml) {
free_xml(expanded_xml);
xml_obj = orig_xml;
}
if (any_sets == FALSE) {
return unpack_simple_rsc_order(xml_obj, data_set);
}
return TRUE;
}
static gboolean
unpack_colocation_set(xmlNode * set, int score, pe_working_set_t * data_set)
{
xmlNode *xml_rsc = NULL;
resource_t *with = NULL;
resource_t *resource = NULL;
const char *set_id = ID(set);
const char *role = crm_element_value(set, "role");
const char *sequential = crm_element_value(set, "sequential");
int local_score = score;
const char *score_s = crm_element_value(set, XML_RULE_ATTR_SCORE);
if (score_s) {
local_score = char2score(score_s);
}
if (sequential != NULL && crm_is_true(sequential) == FALSE) {
return TRUE;
} else if (local_score >= 0) {
for (xml_rsc = __xml_first_child(set); xml_rsc != NULL; xml_rsc = __xml_next(xml_rsc)) {
if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
EXPAND_CONSTRAINT_IDREF(set_id, resource, ID(xml_rsc));
if (with != NULL) {
crm_trace("Colocating %s with %s", resource->id, with->id);
rsc_colocation_new(set_id, NULL, local_score, resource, with, role, role,
data_set);
}
with = resource;
}
}
} else {
/* Anti-colocating with every prior resource is
* the only way to ensure the intuitive result
* (ie. that no-one in the set can run with anyone
* else in the set)
*/
for (xml_rsc = __xml_first_child(set); xml_rsc != NULL; xml_rsc = __xml_next(xml_rsc)) {
if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
xmlNode *xml_rsc_with = NULL;
EXPAND_CONSTRAINT_IDREF(set_id, resource, ID(xml_rsc));
for (xml_rsc_with = __xml_first_child(set); xml_rsc_with != NULL;
xml_rsc_with = __xml_next(xml_rsc_with)) {
if (crm_str_eq((const char *)xml_rsc_with->name, XML_TAG_RESOURCE_REF, TRUE)) {
if (safe_str_eq(resource->id, ID(xml_rsc_with))) {
break;
} else if (resource == NULL) {
crm_config_err("%s: No resource found for %s", set_id,
ID(xml_rsc_with));
return FALSE;
}
EXPAND_CONSTRAINT_IDREF(set_id, with, ID(xml_rsc_with));
crm_trace("Anti-Colocating %s with %s", resource->id, with->id);
rsc_colocation_new(set_id, NULL, local_score, resource, with, role, role,
data_set);
}
}
}
}
}
return TRUE;
}
static gboolean
colocate_rsc_sets(const char *id, xmlNode * set1, xmlNode * set2, int score,
pe_working_set_t * data_set)
{
xmlNode *xml_rsc = NULL;
resource_t *rsc_1 = NULL;
resource_t *rsc_2 = NULL;
const char *role_1 = crm_element_value(set1, "role");
const char *role_2 = crm_element_value(set2, "role");
const char *sequential_1 = crm_element_value(set1, "sequential");
const char *sequential_2 = crm_element_value(set2, "sequential");
if (crm_is_true(sequential_1)) {
/* get the first one */
for (xml_rsc = __xml_first_child(set1); xml_rsc != NULL; xml_rsc = __xml_next(xml_rsc)) {
if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
break;
}
}
}
if (crm_is_true(sequential_2)) {
/* get the last one */
const char *rid = NULL;
for (xml_rsc = __xml_first_child(set2); xml_rsc != NULL; xml_rsc = __xml_next(xml_rsc)) {
if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
rid = ID(xml_rsc);
}
}
EXPAND_CONSTRAINT_IDREF(id, rsc_2, rid);
}
if (rsc_1 != NULL && rsc_2 != NULL) {
rsc_colocation_new(id, NULL, score, rsc_1, rsc_2, role_1, role_2, data_set);
} else if (rsc_1 != NULL) {
for (xml_rsc = __xml_first_child(set2); xml_rsc != NULL; xml_rsc = __xml_next(xml_rsc)) {
if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
EXPAND_CONSTRAINT_IDREF(id, rsc_2, ID(xml_rsc));
rsc_colocation_new(id, NULL, score, rsc_1, rsc_2, role_1, role_2, data_set);
}
}
} else if (rsc_2 != NULL) {
for (xml_rsc = __xml_first_child(set1); xml_rsc != NULL; xml_rsc = __xml_next(xml_rsc)) {
if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
rsc_colocation_new(id, NULL, score, rsc_1, rsc_2, role_1, role_2, data_set);
}
}
} else {
for (xml_rsc = __xml_first_child(set1); xml_rsc != NULL; xml_rsc = __xml_next(xml_rsc)) {
if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
xmlNode *xml_rsc_2 = NULL;
EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
for (xml_rsc_2 = __xml_first_child(set2); xml_rsc_2 != NULL;
xml_rsc_2 = __xml_next(xml_rsc_2)) {
if (crm_str_eq((const char *)xml_rsc_2->name, XML_TAG_RESOURCE_REF, TRUE)) {
EXPAND_CONSTRAINT_IDREF(id, rsc_2, ID(xml_rsc_2));
rsc_colocation_new(id, NULL, score, rsc_1, rsc_2, role_1, role_2, data_set);
}
}
}
}
}
return TRUE;
}
static gboolean
unpack_simple_colocation(xmlNode * xml_obj, pe_working_set_t * data_set)
{
int score_i = 0;
const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
const char *id_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE);
const char *id_rh = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET);
const char *state_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE);
const char *state_rh = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET_ROLE);
const char *instance_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_INSTANCE);
const char *instance_rh = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET_INSTANCE);
const char *attr = crm_element_value(xml_obj, XML_COLOC_ATTR_NODE_ATTR);
const char *symmetrical = crm_element_value(xml_obj, XML_CONS_ATTR_SYMMETRICAL);
resource_t *rsc_lh = pe_find_resource(data_set->resources, id_lh);
resource_t *rsc_rh = pe_find_resource(data_set->resources, id_rh);
if (rsc_lh == NULL) {
crm_config_err("Invalid constraint '%s': No resource named '%s'", id, id_lh);
return FALSE;
} else if (rsc_rh == NULL) {
crm_config_err("Invalid constraint '%s': No resource named '%s'", id, id_rh);
return FALSE;
} else if (instance_lh && rsc_lh->variant < pe_clone) {
crm_config_err
("Invalid constraint '%s': Resource '%s' is not a clone but instance %s was requested",
id, id_lh, instance_lh);
return FALSE;
} else if (instance_rh && rsc_rh->variant < pe_clone) {
crm_config_err
("Invalid constraint '%s': Resource '%s' is not a clone but instance %s was requested",
id, id_rh, instance_rh);
return FALSE;
}
if (instance_lh) {
rsc_lh = find_clone_instance(rsc_lh, instance_lh, data_set);
if (rsc_lh == NULL) {
crm_config_warn("Invalid constraint '%s': No instance '%s' of '%s'", id, instance_lh,
id_lh);
return FALSE;
}
}
if (instance_rh) {
rsc_rh = find_clone_instance(rsc_rh, instance_rh, data_set);
if (rsc_rh == NULL) {
crm_config_warn("Invalid constraint '%s': No instance '%s' of '%s'", id, instance_rh,
id_rh);
return FALSE;
}
}
if (crm_is_true(symmetrical)) {
crm_config_warn("The %s colocation constraint attribute has been removed."
" It didn't do what you think it did anyway.", XML_CONS_ATTR_SYMMETRICAL);
}
if (score) {
score_i = char2score(score);
}
rsc_colocation_new(id, attr, score_i, rsc_lh, rsc_rh, state_lh, state_rh, data_set);
return TRUE;
}
static gboolean
unpack_colocation_template(xmlNode *xml_obj, xmlNode **expanded_xml, pe_working_set_t *data_set)
{
const char *id = NULL;
const char *id_lh = NULL;
const char *id_rh = NULL;
const char *state_lh = NULL;
const char *state_rh = NULL;
resource_t *rsc_lh = NULL;
resource_t *rsc_rh = NULL;
xmlNode *template_rsc_set_lh = NULL;
xmlNode *template_rsc_set_rh = NULL;
xmlNode *new_xml = NULL;
xmlNode *rsc_set_lh = NULL;
xmlNode *rsc_set_rh = NULL;
gboolean any_sets = FALSE;
*expanded_xml = NULL;
if(xml_obj == NULL) {
crm_config_err("No constraint object to process.");
return FALSE;
}
id = crm_element_value(xml_obj, XML_ATTR_ID);
if(id == NULL) {
crm_config_err("%s constraint must have an id", crm_element_name(xml_obj));
return FALSE;
}
/* Attempt to expand any template references in possible resource sets. */
expand_templates_in_sets(xml_obj, &new_xml, data_set);
if (new_xml) {
/* There are resource sets referencing templates. Return with the expanded XML. */
crm_log_xml_debug_3(new_xml, "Expanded rsc_colocation...");
*expanded_xml = new_xml;
return TRUE;
}
id_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE);
id_rh = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET);
if(id_lh == NULL || id_rh == NULL) {
return TRUE;
}
rsc_lh = pe_find_resource(data_set->resources, id_lh);
rsc_rh = pe_find_resource(data_set->resources, id_rh);
if(rsc_lh && rsc_rh) {
/* Neither side references any template. */
return TRUE;
}
if (rsc_lh == NULL) {
gboolean rc = g_hash_table_lookup_extended(data_set->template_rsc_sets, id_lh,
NULL, (gpointer) &template_rsc_set_lh);
if (rc == FALSE) {
crm_config_err("Invalid constraint '%s': No resource or template named '%s'", id, id_lh);
return FALSE;
} else if (template_rsc_set_lh == NULL) {
crm_config_warn("Constraint '%s': No resource is derived from template '%s'", id, id_lh);
return FALSE;
}
}
if (rsc_rh == NULL) {
gboolean rc = g_hash_table_lookup_extended(data_set->template_rsc_sets, id_rh,
NULL, (gpointer) &template_rsc_set_rh);
if (rc == FALSE) {
crm_config_err("Invalid constraint '%s': No resource or template named '%s'", id, id_rh);
return FALSE;
} else if (template_rsc_set_rh == NULL) {
crm_config_warn("Constraint '%s': No resource is derived from template '%s'", id, id_rh);
return FALSE;
}
}
if(template_rsc_set_lh && template_rsc_set_rh) {
/* A colocation constraint between two templates makes no sense. */
crm_config_err("Either LHS or RHS of %s should be a normal resource instead of a template", id);
return FALSE;
}
state_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE);
state_rh = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET_ROLE);
new_xml = copy_xml(xml_obj);
/* Convert the template reference in "rsc" into a resource_set under the colocation constraint. */
if(template_to_set(new_xml, &rsc_set_lh, XML_COLOC_ATTR_SOURCE,
TRUE, data_set) == FALSE) {
free_xml(new_xml);
return FALSE;
}
if(rsc_set_lh) {
if(state_lh) {
/* A "rsc-role" is specified.
Move it into the converted resource_set as a "role"" attribute. */
crm_xml_add(rsc_set_lh, "role", state_lh);
xml_remove_prop(new_xml, XML_COLOC_ATTR_SOURCE_ROLE);
}
any_sets = TRUE;
}
/* Convert the template reference in "with-rsc" into a resource_set under the colocation constraint. */
if(template_to_set(new_xml, &rsc_set_rh, XML_COLOC_ATTR_TARGET,
TRUE, data_set) == FALSE) {
free_xml(new_xml);
return FALSE;
}
if(rsc_set_rh) {
if(state_rh) {
/* A "with-rsc-role" is specified.
Move it into the converted resource_set as a "role"" attribute. */
crm_xml_add(rsc_set_rh, "role", state_rh);
xml_remove_prop(new_xml, XML_COLOC_ATTR_TARGET_ROLE);
}
any_sets = TRUE;
}
if(any_sets) {
crm_log_xml_trace(new_xml, "Expanded rsc_colocation...");
*expanded_xml = new_xml;
} else {
free_xml(new_xml);
}
return TRUE;
}
gboolean
unpack_rsc_colocation(xmlNode * xml_obj, pe_working_set_t * data_set)
{
int score_i = 0;
xmlNode *set = NULL;
xmlNode *last = NULL;
gboolean any_sets = FALSE;
xmlNode *orig_xml = NULL;
xmlNode *expanded_xml = NULL;
const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
gboolean rc = TRUE;
if (score) {
score_i = char2score(score);
}
rc = unpack_colocation_template(xml_obj, &expanded_xml, data_set);
if(expanded_xml) {
orig_xml = xml_obj;
xml_obj = expanded_xml;
} else if (rc == FALSE) {
return FALSE;
}
for (set = __xml_first_child(xml_obj); set != NULL; set = __xml_next(set)) {
if (crm_str_eq((const char *)set->name, XML_CONS_TAG_RSC_SET, TRUE)) {
any_sets = TRUE;
set = expand_idref(set, data_set->input);
if (unpack_colocation_set(set, score_i, data_set) == FALSE) {
return FALSE;
} else if (last && colocate_rsc_sets(id, last, set, score_i, data_set) == FALSE) {
return FALSE;
}
last = set;
}
}
if(expanded_xml) {
free_xml(expanded_xml);
xml_obj = orig_xml;
}
if (any_sets == FALSE) {
return unpack_simple_colocation(xml_obj, data_set);
}
return TRUE;
}
gboolean
rsc_ticket_new(const char *id, resource_t * rsc_lh, ticket_t * ticket,
const char *state_lh, const char *loss_policy, pe_working_set_t * data_set)
{
rsc_ticket_t *new_rsc_ticket = NULL;
if (rsc_lh == NULL) {
crm_config_err("No resource found for LHS %s", id);
return FALSE;
}
crm_malloc0(new_rsc_ticket, sizeof(rsc_ticket_t));
if (new_rsc_ticket == NULL) {
return FALSE;
}
if (state_lh == NULL || safe_str_eq(state_lh, RSC_ROLE_STARTED_S)) {
state_lh = RSC_ROLE_UNKNOWN_S;
}
new_rsc_ticket->id = id;
new_rsc_ticket->ticket = ticket;
new_rsc_ticket->rsc_lh = rsc_lh;
new_rsc_ticket->role_lh = text2role(state_lh);
if (safe_str_eq(loss_policy, "fence")) {
crm_debug("On loss of ticket '%s': Fence the nodes running %s (%s)",
new_rsc_ticket->ticket->id, new_rsc_ticket->rsc_lh->id,
role2text(new_rsc_ticket->role_lh));
new_rsc_ticket->loss_policy = loss_ticket_fence;
} else if (safe_str_eq(loss_policy, "freeze")) {
crm_debug("On loss of ticket '%s': Freeze %s (%s)",
new_rsc_ticket->ticket->id, new_rsc_ticket->rsc_lh->id,
role2text(new_rsc_ticket->role_lh));
new_rsc_ticket->loss_policy = loss_ticket_freeze;
} else if (safe_str_eq(loss_policy, "demote")) {
crm_debug("On loss of ticket '%s': Demote %s (%s)",
new_rsc_ticket->ticket->id, new_rsc_ticket->rsc_lh->id,
role2text(new_rsc_ticket->role_lh));
new_rsc_ticket->loss_policy = loss_ticket_demote;
} else if (safe_str_eq(loss_policy, "stop")) {
crm_debug("On loss of ticket '%s': Stop %s (%s)",
new_rsc_ticket->ticket->id, new_rsc_ticket->rsc_lh->id,
role2text(new_rsc_ticket->role_lh));
new_rsc_ticket->loss_policy = loss_ticket_stop;
} else {
if (new_rsc_ticket->role_lh == RSC_ROLE_MASTER) {
crm_debug("On loss of ticket '%s': Default to demote %s (%s)",
new_rsc_ticket->ticket->id, new_rsc_ticket->rsc_lh->id,
role2text(new_rsc_ticket->role_lh));
new_rsc_ticket->loss_policy = loss_ticket_demote;
} else {
crm_debug("On loss of ticket '%s': Default to stop %s (%s)",
new_rsc_ticket->ticket->id, new_rsc_ticket->rsc_lh->id,
role2text(new_rsc_ticket->role_lh));
new_rsc_ticket->loss_policy = loss_ticket_stop;
}
}
crm_trace("%s (%s) ==> %s", rsc_lh->id, role2text(new_rsc_ticket->role_lh), ticket->id);
rsc_lh->rsc_tickets = g_list_append(rsc_lh->rsc_tickets, new_rsc_ticket);
data_set->ticket_constraints = g_list_append(data_set->ticket_constraints, new_rsc_ticket);
return TRUE;
}
static gboolean
unpack_rsc_ticket_set(xmlNode * set, ticket_t * ticket, const char *loss_policy,
pe_working_set_t * data_set)
{
xmlNode *xml_rsc = NULL;
resource_t *resource = NULL;
const char *set_id = ID(set);
const char *role = crm_element_value(set, "role");
if (set == NULL) {
crm_config_err("No resource_set object to process.");
return FALSE;
}
if (set_id == NULL) {
crm_config_err("resource_set must have an id");
return FALSE;
}
if (ticket == NULL) {
crm_config_err("No dependented ticket specified for '%s'", set_id);
return FALSE;
}
for (xml_rsc = __xml_first_child(set); xml_rsc != NULL; xml_rsc = __xml_next(xml_rsc)) {
if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
EXPAND_CONSTRAINT_IDREF(set_id, resource, ID(xml_rsc));
crm_trace("Resource '%s' depends on ticket '%s'", resource->id, ticket->id);
rsc_ticket_new(set_id, resource, ticket, role, loss_policy, data_set);
}
}
return TRUE;
}
static gboolean
unpack_simple_rsc_ticket(xmlNode * xml_obj, pe_working_set_t * data_set)
{
const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
const char *ticket_str = crm_element_value(xml_obj, XML_TICKET_ATTR_TICKET);
const char *loss_policy = crm_element_value(xml_obj, XML_TICKET_ATTR_LOSS_POLICY);
ticket_t *ticket = NULL;
const char *id_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE);
const char *state_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE);
const char *instance_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_INSTANCE);
resource_t *rsc_lh = NULL;
if (xml_obj == NULL) {
crm_config_err("No rsc_ticket constraint object to process.");
return FALSE;
}
if (id == NULL) {
crm_config_err("%s constraint must have an id", crm_element_name(xml_obj));
return FALSE;
}
if (ticket_str == NULL) {
crm_config_err("Invalid constraint '%s': No ticket specified", id);
return FALSE;
} else {
ticket = g_hash_table_lookup(data_set->tickets, ticket_str);
}
if (ticket == NULL) {
crm_config_err("Invalid constraint '%s': No ticket named '%s'", id, ticket_str);
return FALSE;
}
if (id_lh == NULL) {
crm_config_err("Invalid constraint '%s': No resource specified", id);
return FALSE;
} else {
rsc_lh = pe_find_resource(data_set->resources, id_lh);
}
if (rsc_lh == NULL) {
crm_config_err("Invalid constraint '%s': No resource named '%s'", id, id_lh);
return FALSE;
} else if (instance_lh && rsc_lh->variant < pe_clone) {
crm_config_err
("Invalid constraint '%s': Resource '%s' is not a clone but instance %s was requested",
id, id_lh, instance_lh);
return FALSE;
}
if (instance_lh) {
rsc_lh = find_clone_instance(rsc_lh, instance_lh, data_set);
if (rsc_lh == NULL) {
crm_config_warn("Invalid constraint '%s': No instance '%s' of '%s'", id, instance_lh,
id_lh);
return FALSE;
}
}
rsc_ticket_new(id, rsc_lh, ticket, state_lh, loss_policy, data_set);
return TRUE;
}
static gboolean
unpack_rsc_ticket_template(xmlNode *xml_obj, xmlNode **expanded_xml, pe_working_set_t *data_set)
{
const char *id = NULL;
const char *id_lh = NULL;
const char *state_lh = NULL;
resource_t *rsc_lh = NULL;
xmlNode *template_rsc_set_lh = NULL;
xmlNode *new_xml = NULL;
xmlNode *rsc_set_lh = NULL;
gboolean any_sets = FALSE;
*expanded_xml = NULL;
if(xml_obj == NULL) {
crm_config_err("No constraint object to process.");
return FALSE;
}
id = crm_element_value(xml_obj, XML_ATTR_ID);
if(id == NULL) {
crm_config_err("%s constraint must have an id", crm_element_name(xml_obj));
return FALSE;
}
/* Attempt to expand any template references in possible resource sets. */
expand_templates_in_sets(xml_obj, &new_xml, data_set);
if (new_xml) {
/* There are resource sets referencing templates. Return with the expanded XML. */
crm_log_xml_debug_3(new_xml, "Expanded rsc_ticket...");
*expanded_xml = new_xml;
return TRUE;
}
id_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE);
if(id_lh == NULL) {
return TRUE;
}
rsc_lh = pe_find_resource(data_set->resources, id_lh);
if(rsc_lh) {
/* No template is referenced. */
return TRUE;
}
if(g_hash_table_lookup_extended(data_set->template_rsc_sets, id_lh,
NULL, (gpointer) &template_rsc_set_lh) == FALSE) {
crm_config_err("Invalid constraint '%s': No resource or template named '%s'", id, id_lh);
return FALSE;
} else if (template_rsc_set_lh == NULL) {
crm_config_warn("Constraint '%s': No resource is derived from template '%s'", id, id_lh);
return FALSE;
}
state_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE);
new_xml = copy_xml(xml_obj);
/* Convert the template reference in "rsc" into a resource_set under the rsc_ticket constraint. */
if(template_to_set(new_xml, &rsc_set_lh, XML_COLOC_ATTR_SOURCE,
FALSE, data_set) == FALSE) {
free_xml(new_xml);
return FALSE;
}
if(rsc_set_lh) {
if(state_lh) {
/* A "rsc-role" is specified.
Move it into the converted resource_set as a "role"" attribute. */
crm_xml_add(rsc_set_lh, "role", state_lh);
xml_remove_prop(new_xml, XML_COLOC_ATTR_SOURCE_ROLE);
}
any_sets = TRUE;
}
if(any_sets) {
crm_log_xml_trace(new_xml, "Expanded rsc_ticket...");
*expanded_xml = new_xml;
} else {
free_xml(new_xml);
}
return TRUE;
}
gboolean
unpack_rsc_ticket(xmlNode * xml_obj, pe_working_set_t * data_set)
{
xmlNode *set = NULL;
gboolean any_sets = FALSE;
const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
const char *ticket_str = crm_element_value(xml_obj, XML_TICKET_ATTR_TICKET);
const char *loss_policy = crm_element_value(xml_obj, XML_TICKET_ATTR_LOSS_POLICY);
ticket_t *ticket = NULL;
xmlNode *orig_xml = NULL;
xmlNode *expanded_xml = NULL;
gboolean rc = TRUE;
if (xml_obj == NULL) {
crm_config_err("No rsc_ticket constraint object to process.");
return FALSE;
}
if (id == NULL) {
crm_config_err("%s constraint must have an id", crm_element_name(xml_obj));
return FALSE;
}
if (ticket_str == NULL) {
crm_config_err("Invalid constraint '%s': No ticket specified", id);
return FALSE;
} else {
ticket = g_hash_table_lookup(data_set->tickets, ticket_str);
}
if (ticket == NULL) {
- crm_malloc0(ticket, sizeof(ticket_t));
+ ticket = ticket_new(ticket_str, data_set);
if (ticket == NULL) {
- crm_config_err("Cannot allocate ticket '%s'", ticket_str);
return FALSE;
}
-
- ticket->id = crm_strdup(ticket_str);
- ticket->granted = FALSE;
- ticket->last_granted = -1;
-
- g_hash_table_insert(data_set->tickets, crm_strdup(ticket->id), ticket);
}
rc = unpack_rsc_ticket_template(xml_obj, &expanded_xml, data_set);
if(expanded_xml) {
orig_xml = xml_obj;
xml_obj = expanded_xml;
} else if (rc == FALSE) {
return FALSE;
}
for (set = __xml_first_child(xml_obj); set != NULL; set = __xml_next(set)) {
if (crm_str_eq((const char *)set->name, XML_CONS_TAG_RSC_SET, TRUE)) {
any_sets = TRUE;
set = expand_idref(set, data_set->input);
if (unpack_rsc_ticket_set(set, ticket, loss_policy, data_set) == FALSE) {
return FALSE;
}
}
}
if(expanded_xml) {
free_xml(expanded_xml);
xml_obj = orig_xml;
}
if (any_sets == FALSE) {
return unpack_simple_rsc_ticket(xml_obj, data_set);
}
return TRUE;
}
gboolean
is_active(rsc_to_node_t * cons)
{
return TRUE;
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Jun 26, 5:20 PM (16 h, 38 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1959223
Default Alt Text
(243 KB)

Event Timeline