Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/include/crm/msg_xml.h b/include/crm/msg_xml.h
index c75d72410d..0b2b2edd0a 100644
--- a/include/crm/msg_xml.h
+++ b/include/crm/msg_xml.h
@@ -1,278 +1,280 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#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_JOIN_ID "join_id"
#define F_CRM_ELECTION_ID "election-id"
#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_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_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_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_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_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_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"
#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)
#endif
diff --git a/include/crm/pengine/status.h b/include/crm/pengine/status.h
index b16729af3f..8396807348 100644
--- a/include/crm/pengine/status.h
+++ b/include/crm/pengine/status.h
@@ -1,271 +1,272 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#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 action_s action_t;
typedef struct action_s pe_action_t;
typedef struct resource_s resource_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
};
#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
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;
GListPtr nodes;
GListPtr resources;
GListPtr placement_constraints;
GListPtr ordering_constraints;
GListPtr colocation_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;
} 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_notify 0x00000010ULL
#define pe_rsc_unique 0x00000020ULL
#define pe_rsc_can_migrate 0x00000040ULL
#define pe_rsc_provisional 0x00000100ULL
#define pe_rsc_allocating 0x00000200ULL
#define pe_rsc_merging 0x00000400ULL
#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
struct resource_s {
char *id;
char *clone_name;
char *long_name;
xmlNode *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* */
node_t *allocated_to;
GListPtr running_on; /* node_t* */
GListPtr known_on; /* node_t* */
GListPtr 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* */
};
struct action_s
{
int id;
int priority;
resource_t *rsc;
void *rsc_opaque;
node_t *node;
char *task;
char *uuid;
xmlNode *op_entry;
gboolean pseudo;
gboolean runnable;
gboolean optional;
gboolean print_always;
gboolean have_node_attrs;
gboolean failure_is_fatal;
gboolean implied_by_stonith;
gboolean allow_reload_conversion;
enum rsc_start_requirement needs;
enum action_fail_response on_fail;
enum rsc_role_e fail_role;
gboolean dumped;
gboolean processed;
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;
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/cib/cib_utils.c b/lib/cib/cib_utils.c
index 71d6a2f5cd..20c9ed49d2 100644
--- a/lib/cib/cib_utils.c
+++ b/lib/cib/cib_utils.c
@@ -1,894 +1,895 @@
/*
* Copyright (c) 2004 International Business Machines
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <crm_internal.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <sys/utsname.h>
#include <glib.h>
#include <crm/crm.h>
#include <crm/cib.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <lib/cib/cib_private.h>
struct config_root_s
{
const char *name;
const char *parent;
const char *path;
};
/*
* "//crm_config" will also work in place of "/cib/configuration/crm_config"
* The / prefix means find starting from the root, whereas the // prefix means
* find anywhere and risks multiple matches
*/
struct config_root_s known_paths[] = {
{ NULL, NULL, "//cib" },
{ XML_TAG_CIB, NULL, "//cib" },
{ XML_CIB_TAG_STATUS, "/cib", "//cib/status" },
{ XML_CIB_TAG_CONFIGURATION,"/cib", "//cib/configuration" },
{ XML_CIB_TAG_CRMCONFIG, "/cib/configuration", "//cib/configuration/crm_config" },
{ XML_CIB_TAG_NODES, "/cib/configuration", "//cib/configuration/nodes" },
+ { XML_CIB_TAG_DOMAINS, "/cib/configuration", "//cib/configuration/domains" },
{ XML_CIB_TAG_RESOURCES, "/cib/configuration", "//cib/configuration/resources" },
{ XML_CIB_TAG_CONSTRAINTS, "/cib/configuration", "//cib/configuration/constraints" },
{ XML_CIB_TAG_OPCONFIG, "/cib/configuration", "//cib/configuration/op_defaults" },
{ XML_CIB_TAG_RSCCONFIG, "/cib/configuration", "//cib/configuration/rsc_defaults" },
{ XML_CIB_TAG_SECTION_ALL, NULL, "//cib" },
};
const char *
cib_error2string(enum cib_errors return_code)
{
const char *error_msg = NULL;
switch(return_code) {
case cib_bad_permissions:
error_msg = "bad permissions for the on-disk configuration. shutdown heartbeat and repair.";
break;
case cib_bad_digest:
error_msg = "the on-disk configuration was manually altered. shutdown heartbeat and repair.";
break;
case cib_bad_config:
error_msg = "the on-disk configuration is not valid";
break;
case cib_msg_field_add:
error_msg = "failed adding field to cib message";
break;
case cib_id_check:
error_msg = "missing id or id-collision detected";
break;
case cib_operation:
error_msg = "invalid operation";
break;
case cib_create_msg:
error_msg = "couldnt create cib message";
break;
case cib_client_gone:
error_msg = "client left before we could send reply";
break;
case cib_not_connected:
error_msg = "not connected";
break;
case cib_not_authorized:
error_msg = "not authorized";
break;
case cib_send_failed:
error_msg = "send failed";
break;
case cib_reply_failed:
error_msg = "reply failed";
break;
case cib_return_code:
error_msg = "no return code";
break;
case cib_output_ptr:
error_msg = "nowhere to store output";
break;
case cib_output_data:
error_msg = "corrupt output data";
break;
case cib_connection:
error_msg = "connection failed";
break;
case cib_callback_register:
error_msg = "couldnt register callback channel";
break;
case cib_authentication:
error_msg = "";
break;
case cib_registration_msg:
error_msg = "invalid registration msg";
break;
case cib_callback_token:
error_msg = "callback token not found";
break;
case cib_missing:
error_msg = "cib object missing";
break;
case cib_variant:
error_msg = "unknown/corrupt cib variant";
break;
case CIBRES_MISSING_ID:
error_msg = "The id field is missing";
break;
case CIBRES_MISSING_TYPE:
error_msg = "The type field is missing";
break;
case CIBRES_MISSING_FIELD:
error_msg = "A required field is missing";
break;
case CIBRES_OBJTYPE_MISMATCH:
error_msg = "CIBRES_OBJTYPE_MISMATCH";
break;
case cib_EXISTS:
error_msg = "The object already exists";
break;
case cib_NOTEXISTS:
error_msg = "The object/attribute does not exist";
break;
case CIBRES_CORRUPT:
error_msg = "The CIB is corrupt";
break;
case cib_NOOBJECT:
error_msg = "The update was empty";
break;
case cib_NOPARENT:
error_msg = "The parent object does not exist";
break;
case cib_NODECOPY:
error_msg = "Failed while copying update";
break;
case CIBRES_OTHER:
error_msg = "CIBRES_OTHER";
break;
case cib_ok:
error_msg = "ok";
break;
case cib_unknown:
error_msg = "Unknown error";
break;
case cib_STALE:
error_msg = "Discarded old update";
break;
case cib_ACTIVATION:
error_msg = "Activation Failed";
break;
case cib_NOSECTION:
error_msg = "Required section was missing";
break;
case cib_NOTSUPPORTED:
error_msg = "The action/feature is not supported";
break;
case cib_not_master:
error_msg = "Local service is not the master instance";
break;
case cib_client_corrupt:
error_msg = "Service client not valid";
break;
case cib_remote_timeout:
error_msg = "Remote node did not respond";
break;
case cib_master_timeout:
error_msg = "No master service is currently active";
break;
case cib_revision_unsupported:
error_msg = "The required CIB revision number is not supported";
break;
case cib_revision_unknown:
error_msg = "The CIB revision number could not be determined";
break;
case cib_missing_data:
error_msg = "Required data for this CIB API call not found";
break;
case cib_no_quorum:
error_msg = "Write requires quorum";
break;
case cib_diff_failed:
error_msg = "Application of an update diff failed";
break;
case cib_diff_resync:
error_msg = "Application of an update diff failed, requesting a full refresh";
break;
case cib_bad_section:
error_msg = "Invalid CIB section specified";
break;
case cib_old_data:
error_msg = "Update was older than existing configuration";
break;
case cib_dtd_validation:
error_msg = "Update does not conform to the configured schema/DTD";
break;
case cib_invalid_argument:
error_msg = "Invalid argument";
break;
case cib_transform_failed:
error_msg = "Schema transform failed";
break;
}
if(error_msg == NULL) {
crm_err("Unknown CIB Error Code: %d", return_code);
error_msg = "<unknown error>";
}
return error_msg;
}
int
cib_section2enum(const char *a_section)
{
if(a_section == NULL || strcasecmp(a_section, "all") == 0) {
return cib_section_all;
} else if(strcasecmp(a_section, XML_CIB_TAG_NODES) == 0) {
return cib_section_nodes;
} else if(strcasecmp(a_section, XML_CIB_TAG_STATUS) == 0) {
return cib_section_status;
} else if(strcasecmp(a_section, XML_CIB_TAG_CONSTRAINTS) == 0) {
return cib_section_constraints;
} else if(strcasecmp(a_section, XML_CIB_TAG_RESOURCES) == 0) {
return cib_section_resources;
} else if(strcasecmp(a_section, XML_CIB_TAG_CRMCONFIG) == 0) {
return cib_section_crmconfig;
}
crm_err("Unknown CIB section: %s", a_section);
return cib_section_none;
}
int
cib_compare_generation(xmlNode *left, xmlNode *right)
{
int lpc = 0;
const char *attributes[] = {
XML_ATTR_GENERATION_ADMIN,
XML_ATTR_GENERATION,
XML_ATTR_NUMUPDATES,
};
crm_log_xml_debug_3(left, "left");
crm_log_xml_debug_3(right, "right");
for(lpc = 0; lpc < DIMOF(attributes); lpc++) {
int int_elem_l = -1;
int int_elem_r = -1;
const char *elem_r = NULL;
const char *elem_l = crm_element_value(left, attributes[lpc]);
if(right != NULL) {
elem_r = crm_element_value(right, attributes[lpc]);
}
if(elem_l != NULL) { int_elem_l = crm_parse_int(elem_l, NULL); }
if(elem_r != NULL) { int_elem_r = crm_parse_int(elem_r, NULL); }
if(int_elem_l < int_elem_r) {
crm_debug_2("%s (%s < %s)", attributes[lpc],
crm_str(elem_l), crm_str(elem_r));
return -1;
} else if(int_elem_l > int_elem_r) {
crm_debug_2("%s (%s > %s)", attributes[lpc],
crm_str(elem_l), crm_str(elem_r));
return 1;
}
}
return 0;
}
xmlNode*
get_cib_copy(cib_t *cib)
{
xmlNode *xml_cib;
int options = cib_scope_local|cib_sync_call;
if(cib->cmds->query(cib, NULL, &xml_cib, options) != cib_ok) {
crm_err("Couldnt retrieve the CIB");
return NULL;
} else if(xml_cib == NULL) {
crm_err("The CIB result was empty");
return NULL;
}
if(safe_str_eq(crm_element_name(xml_cib), XML_TAG_CIB)) {
return xml_cib;
}
free_xml(xml_cib);
return NULL;
}
xmlNode*
cib_get_generation(cib_t *cib)
{
xmlNode *the_cib = get_cib_copy(cib);
xmlNode *generation = create_xml_node(
NULL, XML_CIB_TAG_GENERATION_TUPPLE);
if(the_cib != NULL) {
copy_in_properties(generation, the_cib);
free_xml(the_cib);
}
return generation;
}
void
log_cib_diff(int log_level, xmlNode *diff, const char *function)
{
int add_updates = 0;
int add_epoch = 0;
int add_admin_epoch = 0;
int del_updates = 0;
int del_epoch = 0;
int del_admin_epoch = 0;
if(diff == NULL) {
return;
}
cib_diff_version_details(
diff, &add_admin_epoch, &add_epoch, &add_updates,
&del_admin_epoch, &del_epoch, &del_updates);
if(add_updates != del_updates) {
do_crm_log(log_level, "%s: Diff: --- %d.%d.%d", function,
del_admin_epoch, del_epoch, del_updates);
do_crm_log(log_level, "%s: Diff: +++ %d.%d.%d", function,
add_admin_epoch, add_epoch, add_updates);
} else if(diff != NULL) {
do_crm_log(log_level,
"%s: Local-only Change: %d.%d.%d", function,
add_admin_epoch, add_epoch, add_updates);
}
log_xml_diff(log_level, diff, function);
}
gboolean
cib_version_details(
xmlNode *cib, int *admin_epoch, int *epoch, int *updates)
{
if(cib == NULL) {
*admin_epoch = -1;
*epoch = -1;
*updates = -1;
return FALSE;
} else {
crm_element_value_int(cib, XML_ATTR_GENERATION, epoch);
crm_element_value_int(cib, XML_ATTR_NUMUPDATES, updates);
crm_element_value_int(cib, XML_ATTR_GENERATION_ADMIN, admin_epoch);
}
return TRUE;
}
gboolean
cib_diff_version_details(
xmlNode *diff, int *admin_epoch, int *epoch, int *updates,
int *_admin_epoch, int *_epoch, int *_updates)
{
xmlNode *tmp = NULL;
tmp = find_xml_node(diff, "diff-added", FALSE);
cib_version_details(tmp, admin_epoch, epoch, updates);
tmp = find_xml_node(diff, "diff-removed", FALSE);
cib_version_details(tmp, _admin_epoch, _epoch, _updates);
return TRUE;
}
/*
* The caller should never free the return value
*/
const char *get_object_path(const char *object_type)
{
int lpc = 0;
int max = DIMOF(known_paths);
for(; lpc < max; lpc++) {
if((object_type == NULL && known_paths[lpc].name == NULL)
|| safe_str_eq(object_type, known_paths[lpc].name)) {
return known_paths[lpc].path;
}
}
return NULL;
}
const char *get_object_parent(const char *object_type)
{
int lpc = 0;
int max = DIMOF(known_paths);
for(; lpc < max; lpc++) {
if(safe_str_eq(object_type, known_paths[lpc].name)) {
return known_paths[lpc].parent;
}
}
return NULL;
}
xmlNode*
get_object_root(const char *object_type, xmlNode *the_root)
{
const char *xpath = get_object_path(object_type);
if(xpath == NULL) {
return the_root; /* or return NULL? */
}
return get_xpath_object(xpath, the_root, LOG_DEBUG_2);
}
xmlNode*
create_cib_fragment_adv(
xmlNode *update, const char *update_section, const char *source)
{
xmlNode *cib = NULL;
gboolean whole_cib = FALSE;
xmlNode *object_root = NULL;
char *local_section = NULL;
/* crm_debug("Creating a blank fragment: %s", update_section); */
if(update == NULL && update_section == NULL) {
crm_debug_3("Creating a blank fragment");
update = createEmptyCib();
crm_xml_add(cib, XML_ATTR_ORIGIN, source);
return update;
} else if(update == NULL) {
crm_err("No update to create a fragment for");
return NULL;
}
CRM_CHECK(update_section != NULL, return NULL);
if(safe_str_eq(crm_element_name(update), XML_TAG_CIB)) {
whole_cib = TRUE;
}
if(whole_cib == FALSE) {
cib = createEmptyCib();
crm_xml_add(cib, XML_ATTR_ORIGIN, source);
object_root = get_object_root(update_section, cib);
add_node_copy(object_root, update);
} else {
cib = copy_xml(update);
crm_xml_add(cib, XML_ATTR_ORIGIN, source);
}
crm_free(local_section);
crm_debug_3("Verifying created fragment");
return cib;
}
/*
* It is the callers responsibility to free both the new CIB (output)
* and the new CIB (input)
*/
xmlNode*
createEmptyCib(void)
{
xmlNode *cib_root = NULL, *config = NULL, *status = NULL;
cib_root = create_xml_node(NULL, XML_TAG_CIB);
config = create_xml_node(cib_root, XML_CIB_TAG_CONFIGURATION);
status = create_xml_node(cib_root, XML_CIB_TAG_STATUS);
/* crm_xml_add(cib_root, "version", "1"); */
create_xml_node(config, XML_CIB_TAG_CRMCONFIG);
create_xml_node(config, XML_CIB_TAG_NODES);
create_xml_node(config, XML_CIB_TAG_RESOURCES);
create_xml_node(config, XML_CIB_TAG_CONSTRAINTS);
return cib_root;
}
static unsigned int dtd_throttle = 0;
enum cib_errors
cib_perform_op(const char *op, int call_options, cib_op_t *fn, gboolean is_query,
const char *section, xmlNode *req, xmlNode *input,
gboolean manage_counters, gboolean *config_changed,
xmlNode *current_cib, xmlNode **result_cib, xmlNode **diff, xmlNode **output)
{
int rc = cib_ok;
gboolean check_dtd = TRUE;
xmlNode *scratch = NULL;
xmlNode *local_diff = NULL;
const char *current_dtd = "unknown";
CRM_CHECK(output != NULL, return cib_output_data);
CRM_CHECK(result_cib != NULL, return cib_output_data);
CRM_CHECK(config_changed != NULL, return cib_output_data);
*output = NULL;
*result_cib = NULL;
*config_changed = FALSE;
if(fn == NULL) {
return cib_operation;
}
if(is_query) {
rc = (*fn)(op, call_options, section, req, input, current_cib, result_cib, output);
return rc;
}
scratch = copy_xml(current_cib);
rc = (*fn)(op, call_options, section, req, input, current_cib, &scratch, output);
CRM_CHECK(current_cib != scratch, return cib_unknown);
if(rc == cib_ok && scratch == NULL) {
rc = cib_unknown;
}
if(rc == cib_ok && current_cib) {
int old = 0;
int new = 0;
crm_element_value_int(scratch, XML_ATTR_GENERATION_ADMIN, &new);
crm_element_value_int(current_cib, XML_ATTR_GENERATION_ADMIN, &old);
if(old > new) {
crm_err("%s went backwards: %d -> %d (Opts: 0x%x)",
XML_ATTR_GENERATION_ADMIN, old, new, call_options);
crm_log_xml_warn(req, "Bad Op");
crm_log_xml_warn(input, "Bad Data");
rc = cib_old_data;
} else if(old == new) {
crm_element_value_int(scratch, XML_ATTR_GENERATION, &new);
crm_element_value_int(current_cib, XML_ATTR_GENERATION, &old);
if(old > new) {
crm_err("%s went backwards: %d -> %d (Opts: 0x%x)",
XML_ATTR_GENERATION, old, new, call_options);
crm_log_xml_warn(req, "Bad Op");
crm_log_xml_warn(input, "Bad Data");
rc = cib_old_data;
}
}
}
if(rc == cib_ok) {
fix_plus_plus_recursive(scratch);
current_dtd = crm_element_value(scratch, XML_ATTR_VALIDATION);
if(manage_counters) {
local_diff = diff_xml_object(current_cib, scratch, FALSE);
*config_changed = cib_config_changed(local_diff);
if(*config_changed) {
cib_update_counter(scratch, XML_ATTR_NUMUPDATES, TRUE);
cib_update_counter(scratch, XML_ATTR_GENERATION, FALSE);
} else if(local_diff != NULL){
cib_update_counter(scratch, XML_ATTR_NUMUPDATES, FALSE);
if(dtd_throttle++ % 20) {
check_dtd = FALSE; /* Throttle the amount of costly validation we perform due to status updates
* a) we don't really care whats in the status section
* b) we don't validate any of it's contents at the moment anyway
*/
}
}
}
}
if(diff != NULL && local_diff != NULL) {
/* Only fix the diff if we'll return it... */
xmlNode *cib = NULL;
xmlNode *diff_child = NULL;
const char *tag = NULL;
const char *value = NULL;
tag = "diff-removed";
diff_child = find_xml_node(local_diff, tag, FALSE);
if(diff_child == NULL) {
diff_child = create_xml_node(local_diff, tag);
}
tag = XML_TAG_CIB;
cib = find_xml_node(diff_child, tag, FALSE);
if(cib == NULL) {
cib = create_xml_node(diff_child, tag);
}
tag = XML_ATTR_GENERATION_ADMIN;
value = crm_element_value(current_cib, tag);
crm_xml_add(diff_child, tag, value);
if(*config_changed) {
crm_xml_add(cib, tag, value);
}
tag = XML_ATTR_GENERATION;
value = crm_element_value(current_cib, tag);
crm_xml_add(diff_child, tag, value);
if(*config_changed) {
crm_xml_add(cib, tag, value);
}
tag = XML_ATTR_NUMUPDATES;
value = crm_element_value(current_cib, tag);
crm_xml_add(cib, tag, value);
crm_xml_add(diff_child, tag, value);
tag = "diff-added";
diff_child = find_xml_node(local_diff, tag, FALSE);
if(diff_child == NULL) {
diff_child = create_xml_node(local_diff, tag);
}
tag = XML_TAG_CIB;
cib = find_xml_node(diff_child, tag, FALSE);
if(cib == NULL) {
cib = create_xml_node(diff_child, tag);
}
tag = XML_ATTR_GENERATION_ADMIN;
value = crm_element_value(scratch, tag);
crm_xml_add(diff_child, tag, value);
if(*config_changed) {
crm_xml_add(cib, tag, value);
}
tag = XML_ATTR_GENERATION;
value = crm_element_value(scratch, tag);
crm_xml_add(diff_child, tag, value);
if(*config_changed) {
crm_xml_add(cib, tag, value);
}
tag = XML_ATTR_NUMUPDATES;
value = crm_element_value(scratch, tag);
crm_xml_add(cib, tag, value);
crm_xml_add(diff_child, tag, value);
*diff = local_diff;
local_diff = NULL;
}
if(rc == cib_ok
&& check_dtd
&& validate_xml(scratch, NULL, TRUE) == FALSE) {
crm_warn("Updated CIB does not validate against %s schema/dtd", crm_str(current_dtd));
rc = cib_dtd_validation;
}
*result_cib = scratch;
free_xml(local_diff);
return rc;
}
int get_channel_token(IPC_Channel *ch, char **token)
{
int rc = cib_ok;
xmlNode *reg_msg = NULL;
const char *msg_type = NULL;
const char *tmp_ticket = NULL;
CRM_CHECK(ch != NULL, return cib_missing);
CRM_CHECK(token != NULL, return cib_output_ptr);
crm_debug_4("Waiting for msg on command channel");
reg_msg = xmlfromIPC(ch, MAX_IPC_DELAY);
if(ch->ops->get_chan_status(ch) != IPC_CONNECT) {
crm_err("No reply message - disconnected");
free_xml(reg_msg);
return cib_not_connected;
} else if(reg_msg == NULL) {
crm_err("No reply message - empty");
return cib_reply_failed;
}
msg_type = crm_element_value(reg_msg, F_CIB_OPERATION);
tmp_ticket = crm_element_value(reg_msg, F_CIB_CLIENTID);
if(safe_str_neq(msg_type, CRM_OP_REGISTER) ) {
crm_err("Invalid registration message: %s", msg_type);
rc = cib_registration_msg;
} else if(tmp_ticket == NULL) {
rc = cib_callback_token;
} else {
*token = crm_strdup(tmp_ticket);
}
free_xml(reg_msg);
return rc;
}
xmlNode *
cib_create_op(
int call_id, const char *token, const char *op, const char *host, const char *section,
xmlNode *data, int call_options)
{
int rc = HA_OK;
xmlNode *op_msg = create_xml_node(NULL, "cib_command");
CRM_CHECK(op_msg != NULL, return NULL);
CRM_CHECK(token != NULL, return NULL);
crm_xml_add(op_msg, F_XML_TAGNAME, "cib_command");
crm_xml_add(op_msg, F_TYPE, T_CIB);
crm_xml_add(op_msg, F_CIB_CALLBACK_TOKEN, token);
crm_xml_add(op_msg, F_CIB_OPERATION, op);
crm_xml_add(op_msg, F_CIB_HOST, host);
crm_xml_add(op_msg, F_CIB_SECTION, section);
crm_xml_add_int(op_msg, F_CIB_CALLID, call_id);
crm_debug_4("Sending call options: %.8lx, %d",
(long)call_options, call_options);
crm_xml_add_int(op_msg, F_CIB_CALLOPTS, call_options);
if(data != NULL) {
add_message_xml(op_msg, F_CIB_CALLDATA, data);
}
if (rc != HA_OK) {
crm_err("Failed to create CIB operation message");
crm_log_xml(LOG_ERR, "op", op_msg);
free_xml(op_msg);
return NULL;
}
if(call_options & cib_inhibit_bcast) {
CRM_CHECK((call_options & cib_scope_local), return NULL);
}
return op_msg;
}
void
cib_native_callback(cib_t *cib, xmlNode *msg, int call_id, int rc)
{
xmlNode *output = NULL;
cib_callback_client_t *blob = NULL;
cib_callback_client_t local_blob;
local_blob.id = NULL;
local_blob.callback = NULL;
local_blob.user_data = NULL;
local_blob.only_success = FALSE;
if(msg != NULL) {
crm_element_value_int(msg, F_CIB_RC, &rc);
crm_element_value_int(msg, F_CIB_CALLID, &call_id);
output = get_message_xml(msg, F_CIB_CALLDATA);
}
blob = g_hash_table_lookup(
cib_op_callback_table, GINT_TO_POINTER(call_id));
if(blob != NULL) {
local_blob = *blob;
blob = NULL;
remove_cib_op_callback(call_id, FALSE);
} else {
crm_debug_2("No callback found for call %d", call_id);
local_blob.callback = NULL;
}
if(cib == NULL) {
crm_debug("No cib object supplied");
}
if(rc == cib_diff_resync) {
/* This is an internal value that clients do not and should not care about */
rc = cib_ok;
}
if(local_blob.callback != NULL
&& (rc == cib_ok || local_blob.only_success == FALSE)) {
crm_debug_2("Invoking callback %s for call %d", crm_str(local_blob.id), call_id);
local_blob.callback(msg, call_id, rc, output, local_blob.user_data);
} else if(cib && cib->op_callback == NULL && rc != cib_ok) {
crm_warn("CIB command failed: %s", cib_error2string(rc));
crm_log_xml(LOG_DEBUG, "Failed CIB Update", msg);
}
if(cib && cib->op_callback != NULL) {
crm_debug_2("Invoking global callback for call %d", call_id);
cib->op_callback(msg, call_id, rc, output);
}
crm_debug_4("OP callback activated.");
}
void
cib_native_notify(gpointer data, gpointer user_data)
{
xmlNode *msg = user_data;
cib_notify_client_t *entry = data;
const char *event = NULL;
if(msg == NULL) {
crm_warn("Skipping callback - NULL message");
return;
}
event = crm_element_value(msg, F_SUBTYPE);
if(entry == NULL) {
crm_warn("Skipping callback - NULL callback client");
return;
} else if(entry->callback == NULL) {
crm_warn("Skipping callback - NULL callback");
return;
} else if(safe_str_neq(entry->event, event)) {
crm_debug_4("Skipping callback - event mismatch %p/%s vs. %s",
entry, entry->event, event);
return;
}
crm_debug_4("Invoking callback for %p/%s event...", entry, event);
entry->callback(event, msg);
crm_debug_4("Callback invoked...");
}
gboolean determine_host(cib_t *cib_conn, char **node_uname, char **node_uuid)
{
CRM_CHECK(node_uname != NULL, return FALSE);
if(*node_uname == NULL) {
struct utsname name;
if(uname(&name) < 0) {
crm_perror(LOG_ERR,"uname(2) call failed");
return FALSE;
}
*node_uname = crm_strdup(name.nodename);
crm_info("Detected uname: %s", *node_uname);
}
if(cib_conn && *node_uname != NULL
&& node_uuid != NULL && *node_uuid == NULL) {
int rc = query_node_uuid(cib_conn, *node_uname, node_uuid);
if(rc != cib_ok) {
fprintf(stderr,"Could not map uname=%s to a UUID: %s\n",
*node_uname, cib_error2string(rc));
return FALSE;
}
crm_info("Mapped %s to %s", *node_uname, crm_str(*node_uuid));
}
return TRUE;
}
diff --git a/lib/pengine/status.c b/lib/pengine/status.c
index e3c152bbd5..75f073f5a7 100644
--- a/lib/pengine/status.c
+++ b/lib/pengine/status.c
@@ -1,291 +1,296 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <crm_internal.h>
#include <sys/param.h>
#include <crm/crm.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 <utils.h>
#include <unpack.h>
xmlNode * do_calculations(
pe_working_set_t *data_set, xmlNode *xml_input, ha_time_t *now);
extern xmlNode*get_object_root(
const char *object_type, xmlNode *the_root);
#define MEMCHECK_STAGE_0 0
#define check_and_exit(stage) cleanup_calculations(data_set); \
crm_mem_stats(NULL); \
crm_err("Exiting: stage %d", stage); \
exit(1);
/*
* Unpack everything
* At the end you'll have:
* - A list of nodes
* - A list of resources (each with any dependencies on other resources)
* - A list of constraints between resources and nodes
* - A list of constraints between start/stop actions
* - A list of nodes that need to be stonith'd
* - A list of nodes that need to be shutdown
* - A list of the possible stop/start actions (without dependencies)
*/
gboolean
cluster_status(pe_working_set_t *data_set)
{
xmlNode * config = get_object_root(
XML_CIB_TAG_CRMCONFIG, data_set->input);
xmlNode * cib_nodes = get_object_root(
XML_CIB_TAG_NODES, data_set->input);
xmlNode * cib_resources = get_object_root(
XML_CIB_TAG_RESOURCES, data_set->input);
xmlNode * cib_status = get_object_root(
XML_CIB_TAG_STATUS, data_set->input);
+ xmlNode * cib_domains = get_object_root(
+ XML_CIB_TAG_DOMAINS, data_set->input);
const char *value = crm_element_value(
data_set->input, XML_ATTR_HAVE_QUORUM);
crm_debug_3("Beginning unpack");
/* reset remaining global variables */
if(data_set->input == NULL) {
return FALSE;
}
if(data_set->now == NULL) {
data_set->now = new_ha_date(TRUE);
}
if(data_set->input != NULL
&& crm_element_value(data_set->input, XML_ATTR_DC_UUID) != NULL) {
/* this should always be present */
data_set->dc_uuid = crm_element_value_copy(
data_set->input, XML_ATTR_DC_UUID);
}
clear_bit_inplace(data_set->flags, pe_flag_have_quorum);
if(crm_is_true(value)) {
set_bit_inplace(data_set->flags, pe_flag_have_quorum);
}
data_set->op_defaults = get_object_root(XML_CIB_TAG_OPCONFIG, data_set->input);
data_set->rsc_defaults = get_object_root(XML_CIB_TAG_RSCCONFIG, data_set->input);
unpack_config(config, data_set);
if(is_set(data_set->flags, pe_flag_have_quorum) == FALSE
&& data_set->no_quorum_policy != no_quorum_ignore) {
crm_warn("We do not have quorum"
" - fencing and resource management disabled");
}
unpack_nodes(cib_nodes, data_set);
+ unpack_domains(cib_domains, data_set);
unpack_resources(cib_resources, data_set);
unpack_status(cib_status, data_set);
return TRUE;
}
static void
pe_free_resources(GListPtr resources)
{
resource_t *rsc = NULL;
GListPtr iterator = resources;
while(iterator != NULL) {
iterator = iterator;
rsc = (resource_t *)iterator->data;
iterator = iterator->next;
rsc->fns->free(rsc);
}
if(resources != NULL) {
g_list_free(resources);
}
}
static void
pe_free_actions(GListPtr actions)
{
GListPtr iterator = actions;
while(iterator != NULL) {
pe_free_action(iterator->data);
iterator = iterator->next;
}
if(actions != NULL) {
g_list_free(actions);
}
}
static void
pe_free_nodes(GListPtr nodes)
{
GListPtr iterator = nodes;
while(iterator != NULL) {
node_t *node = (node_t*)iterator->data;
struct node_shared_s *details = node->details;
iterator = iterator->next;
crm_debug_5("deleting node");
crm_debug_5("%s is being deleted", details->uname);
print_node("delete", node, FALSE);
if(details != NULL) {
if(details->attrs != NULL) {
g_hash_table_destroy(details->attrs);
}
if(details->utilization != NULL) {
g_hash_table_destroy(details->utilization);
}
pe_free_shallow_adv(details->running_rsc, FALSE);
pe_free_shallow_adv(details->allocated_rsc, FALSE);
crm_free(details);
}
crm_free(node);
}
if(nodes != NULL) {
g_list_free(nodes);
}
}
void
cleanup_calculations(pe_working_set_t *data_set)
{
if(data_set == NULL) {
return;
}
if(data_set->config_hash != NULL) {
g_hash_table_destroy(data_set->config_hash);
}
crm_free(data_set->dc_uuid);
crm_debug_3("deleting resources");
pe_free_resources(data_set->resources);
crm_debug_3("deleting actions");
pe_free_actions(data_set->actions);
+ g_hash_table_destroy(data_set->domains);
+ data_set->domains = NULL;
+
crm_debug_3("deleting nodes");
pe_free_nodes(data_set->nodes);
free_xml(data_set->graph);
free_ha_date(data_set->now);
free_xml(data_set->input);
free_xml(data_set->failed);
data_set->stonith_action = NULL;
CRM_CHECK(data_set->ordering_constraints == NULL, ;);
CRM_CHECK(data_set->placement_constraints == NULL, ;);
xmlCleanupParser();
}
-
void
set_working_set_defaults(pe_working_set_t *data_set)
{
data_set->failed = create_xml_node(NULL, "failed-ops");
data_set->now = NULL;
data_set->input = NULL;
data_set->graph = NULL;
data_set->dc_uuid = NULL;
data_set->dc_node = NULL;
data_set->nodes = NULL;
data_set->actions = NULL;
data_set->resources = NULL;
data_set->config_hash = NULL;
data_set->stonith_action = NULL;
data_set->ordering_constraints = NULL;
data_set->placement_constraints = NULL;
data_set->colocation_constraints = NULL;
data_set->order_id = 1;
data_set->action_id = 1;
data_set->num_synapse = 0;
data_set->max_valid_nodes = 0;
data_set->no_quorum_policy = no_quorum_freeze;
data_set->default_resource_stickiness = 0;
data_set->flags = 0x0ULL;
set_bit_inplace(data_set->flags, pe_flag_stop_rsc_orphans);
set_bit_inplace(data_set->flags, pe_flag_symmetric_cluster);
set_bit_inplace(data_set->flags, pe_flag_is_managed_default);
set_bit_inplace(data_set->flags, pe_flag_stop_action_orphans);
}
resource_t *
pe_find_resource(GListPtr rsc_list, const char *id)
{
unsigned lpc = 0;
resource_t *rsc = NULL;
resource_t *match = NULL;
if(id == NULL) {
return NULL;
}
for(lpc = 0; lpc < g_list_length(rsc_list); lpc++) {
rsc = g_list_nth_data(rsc_list, lpc);
match = rsc->fns->find_rsc(rsc, id, TRUE, FALSE, NULL, TRUE);
if(match != NULL) {
return match;
}
}
crm_debug_2("No match for %s", id);
return NULL;
}
node_t *
pe_find_node_id(GListPtr nodes, const char *id)
{
slist_iter(node, node_t, nodes, lpc,
if(node && safe_str_eq(node->details->id, id)) {
return node;
}
);
/* error */
return NULL;
}
node_t *
pe_find_node(GListPtr nodes, const char *uname)
{
slist_iter(node, node_t, nodes, lpc,
if(node && safe_str_eq(node->details->uname, uname)) {
return node;
}
);
/* error */
return NULL;
}
diff --git a/lib/pengine/unpack.c b/lib/pengine/unpack.c
index d4751352a2..fefbbf9f9b 100644
--- a/lib/pengine/unpack.c
+++ b/lib/pengine/unpack.c
@@ -1,1754 +1,1815 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <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>
#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,
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(
g_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_debug_2("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_debug_2("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_debug_2("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_debug_2("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_debug_2("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_debug_2("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_debug_2("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_debug_2("Placement strategy: %s", data_set->placement_strategy);
return TRUE;
}
gboolean
unpack_nodes(xmlNode * xml_nodes, pe_working_set_t *data_set)
{
node_t *new_node = NULL;
const char *id = NULL;
const char *uname = NULL;
const char *type = 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");
}
xml_child_iter_filter(
xml_nodes, xml_obj, XML_CIB_TAG_NODE,
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);
crm_debug_3("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 = 0;
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_debug_3("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(
g_str_hash, g_str_equal,
g_hash_destroy_str, g_hash_destroy_str);
new_node->details->utilization = g_hash_table_new_full(
g_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_debug_3("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_destroy(node_t, node, domain, crm_free(node));
+}
+
+gboolean
+unpack_domains(xmlNode *xml_domains, pe_working_set_t *data_set)
+{
+ GListPtr domain = NULL;
+ const char *id = NULL;
+
+ crm_info("Unpacking domains");
+ data_set->domains = g_hash_table_new_full(
+ g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_node_list);
+
+ xml_child_iter_filter(
+ xml_domains, xml_domain, XML_CIB_TAG_DOMAIN,
+
+ domain = NULL;
+ id = crm_element_value(xml_domain, XML_ATTR_ID);
+
+ xml_child_iter_filter(
+ xml_domain, xml_node, XML_CIB_TAG_NODE,
+
+ 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);
+ continue;
+ }
+ 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_append(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;
+}
+
gboolean
unpack_resources(xmlNode * xml_resources, pe_working_set_t *data_set)
{
xml_child_iter(
xml_resources, xml_obj,
resource_t *new_rsc = NULL;
crm_debug_3("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;
}
/* 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;
node_t *this_node = NULL;
crm_debug_3("Beginning unpack");
xml_child_iter_filter(
status, node_state, XML_CIB_TAG_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);
lrm_rsc = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE);
lrm_rsc = find_xml_node(lrm_rsc, XML_LRM_TAG_RESOURCES, FALSE);
crm_debug_3("Processing node %s", 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_debug_3("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");
}
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
*/
crm_debug_3("Processing lrm resource entries");
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_debug_2("Node is down: ha_state=%s, ccm_state=%s",
crm_str(ha_state), crm_str(ccm_state));
} else if(!crm_is_true(ccm_state)
|| safe_str_eq(ha_state, DEADSTATUS)) {
} 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_debug_2("CRMd is down: ha_state=%s, ccm_state=%s",
crm_str(ha_state), crm_str(ccm_state));
crm_debug_2("\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");
this_node->details->shutdown = TRUE;
}
} 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 {
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_debug_2("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);
break;
}
}
return last_rsc_id;
}
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");
common_unpack(xml_rsc, &rsc, NULL, data_set);
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 = crm_strdup(rsc_id);
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_debug_3("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_debug_3("Looking for %s", rsc_id);
rsc = parent->fns->find_rsc(parent, rsc_id, FALSE, FALSE, NULL, TRUE);
} else {
rsc = parent->fns->find_rsc(parent, base, FALSE, TRUE, node, TRUE);
if(rsc != NULL && rsc->running_on) {
rsc = NULL;
crm_debug_3("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:".
*/
slist_iter(child, resource_t, parent->children, lpc,
node_t *loc = child->fns->location(child, NULL, TRUE);
if(loc && loc->details == node->details) {
resource_t *tmp = child->fns->find_rsc(child, base, FALSE, TRUE, NULL, TRUE);
if(tmp && tmp->running_on == NULL) {
rsc = tmp;
break;
}
}
);
goto orphan_check;
}
while(rsc == NULL) {
crm_debug_3("Trying %s", alt_rsc_id);
rsc = parent->fns->find_rsc(parent, alt_rsc_id, FALSE, FALSE, NULL, TRUE);
if(rsc == NULL) {
break;
} else if(rsc->running_on == NULL) {
break;
}
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, FALSE, TRUE, NULL, TRUE);
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_debug_2("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_debug_2("%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 {
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);
}
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)
{
if(on_fail == action_migrate_failure) {
node_t *from = NULL;
const char *uuid = crm_element_value(migrate_op, CRMD_ACTION_MIGRATED);
on_fail = action_fail_recover;
from = pe_find_node_id(data_set->nodes, uuid);
if(from != NULL) {
process_rsc_state(rsc, from, on_fail, NULL, data_set);
} else {
crm_log_xml_err(migrate_op, "Bad Op");
}
}
crm_debug_2("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) {
rsc->known_on = g_list_append(rsc->known_on, node);
}
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);
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;
case action_migrate_failure:
/* anything extra? */
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_debug_2("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);
slist_iter(stop, action_t, possible_matches, lpc,
stop->optional = TRUE;
);
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)
{
const char *task = NULL;
const char *status = NULL;
crm_debug_3("%s: Start index %d, stop index = %d",
rsc->id, start_index, stop_index);
slist_iter(rsc_op, xmlNode, sorted_op_list, lpc,
int interval = 0;
char *key = NULL;
const char *id = ID(rsc_op);
const char *interval_s = NULL;
if(node->details->online == FALSE) {
crm_debug_4("Skipping %s/%s: node is offline",
rsc->id, node->details->uname);
break;
} else if(start_index < stop_index) {
crm_debug_4("Skipping %s/%s: not active",
rsc->id, node->details->uname);
break;
} else if(lpc <= start_index) {
crm_debug_4("Skipping %s/%s: old",
id, node->details->uname);
continue;
}
interval_s = crm_element_value(rsc_op,XML_LRM_ATTR_INTERVAL);
interval = crm_parse_int(interval_s, "0");
if(interval == 0) {
crm_debug_4("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_debug_4("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_debug_3("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)
{
const char *task = NULL;
const char *status = NULL;
*stop_index = -1;
*start_index = -1;
slist_iter(
rsc_op, xmlNode, sorted_op_list, lpc,
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 = lpc;
} else if(safe_str_eq(task, CRMD_ACTION_START)) {
*start_index = lpc;
} 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 = lpc;
}
}
);
}
static void
unpack_lrm_rsc_state(
node_t *node, xmlNode * rsc_entry, pe_working_set_t *data_set)
{
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;
enum action_fail_response on_fail = FALSE;
enum rsc_role_e saved_role = RSC_ROLE_UNKNOWN;
crm_debug_3("[%s] Processing %s on %s",
crm_element_name(rsc_entry), rsc_id, node->details->uname);
/* extract operations */
op_list = NULL;
sorted_op_list = NULL;
xml_child_iter_filter(
rsc_entry, rsc_op, XML_LRM_TAG_RSC_OP,
op_list = g_list_append(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);
slist_iter(
rsc_op, xmlNode, sorted_op_list, lpc,
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, &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)
{
CRM_CHECK(node != NULL, return FALSE);
crm_debug_3("Unpacking resources on %s", node->details->uname);
xml_child_iter_filter(
lrm_rsc_list, rsc_entry, XML_LRM_TAG_RESOURCE,
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;
}
}
gboolean
unpack_rsc_op(resource_t *rsc, node_t *node, xmlNode *xml_op,
enum action_fail_response *on_fail, pe_working_set_t *data_set)
{
const char *id = NULL;
const char *key = NULL;
const char *task = NULL;
const char *magic = NULL;
const char *task_id = NULL;
const char *actual_rc = NULL;
/* const char *target_rc = NULL; */
const char *task_status = NULL;
const char *interval_s = NULL;
const char *op_digest = NULL;
const char *op_version = NULL;
int interval = 0;
int task_status_i = -2;
int actual_rc_i = 0;
int target_rc = -1;
action_t *action = NULL;
node_t *effective_node = NULL;
resource_t *failed = NULL;
gboolean expired = FALSE;
gboolean is_probe = 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_id = crm_element_value(xml_op, XML_LRM_ATTR_CALLID);
task_status = crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS);
op_digest = crm_element_value(xml_op, XML_LRM_ATTR_OP_DIGEST);
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_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-run", &last_run) == 0) {
/* int last_change = crm_element_value_int(xml_op, "last_rc_change"); */
time_t now = get_timet_now(data_set);
if(now > (last_run + rsc->failure_timeout)) {
expired = TRUE;
}
}
}
crm_debug_2("Unpacking task %s/%s (call_id=%s, 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_debug_2("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, NULL, data_set) == 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",
id, 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,
"Hard error - %s failed with rc=%d: Preventing %s from re-starting %s %s",
id, actual_rc_i, failed->id,
effective_node?"on":"anywhere",
effective_node?effective_node->details->uname:"in the cluster");
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);
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_notice("Operation %s found resource %s active on %s",
id, 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;
}
}
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;
}
break;
case LRM_OP_DONE:
crm_debug_3("%s/%s completed on %s",
rsc->id, task, node->details->uname);
if(actual_rc_i == EXECRA_NOT_RUNNING) {
/* nothing to do */
} else if(safe_str_eq(task, CRMD_ACTION_STOP)) {
rsc->role = RSC_ROLE_STOPPED;
/* clear any previous failure actions */
switch(*on_fail) {
case action_fail_block:
case action_fail_stop:
case action_fail_fence:
case action_fail_migrate:
case action_fail_standby:
crm_debug_2("%s.%s is not cleared by a completed stop",
rsc->id, fail2text(*on_fail));
break;
case action_fail_ignore:
case action_fail_recover:
case action_migrate_failure:
*on_fail = action_fail_ignore;
rsc->next_role = RSC_ROLE_UNKNOWN;
}
} else if(safe_str_eq(task, CRMD_ACTION_PROMOTE)) {
rsc->role = RSC_ROLE_MASTER;
} else if(safe_str_eq(task, CRMD_ACTION_DEMOTE)) {
rsc->role = RSC_ROLE_SLAVE;
} else if(rsc->role < RSC_ROLE_STARTED) {
crm_debug_3("%s active on %s",
rsc->id, node->details->uname);
set_active(rsc);
}
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);
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_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_debug_2("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) {
crm_err("Making sure %s doesn't come up again", rsc->id);
/* make sure it doesnt come up again */
pe_free_shallow_adv(rsc->allowed_nodes, TRUE);
rsc->allowed_nodes = node_list_dup(
data_set->nodes, FALSE, FALSE);
slist_iter(
node, node_t, rsc->allowed_nodes, lpc,
node->weight = -INFINITY;
);
}
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_debug_3("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 stop_index = -1;
int start_index = -1;
GListPtr op_list = NULL;
GListPtr sorted_op_list = NULL;
/* extract operations */
op_list = NULL;
sorted_op_list = NULL;
xml_child_iter_filter(
rsc_entry, rsc_op, XML_LRM_TAG_RSC_OP,
crm_xml_add(rsc_op, "resource", rsc);
crm_xml_add(rsc_op, XML_ATTR_UNAME, node);
op_list = g_list_append(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);
slist_iter(rsc_op, xmlNode, sorted_op_list, lpc,
if(start_index < stop_index) {
crm_debug_4("Skipping %s: not active", ID(rsc_entry));
break;
} else if(lpc < start_index) {
crm_debug_4("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;
xml_child_iter_filter(
status, node_state, XML_CIB_TAG_STATE,
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
*/
tmp = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE);
tmp = find_xml_node(tmp, XML_LRM_TAG_RESOURCES, FALSE);
xml_child_iter_filter(
tmp, lrm_rsc, XML_LRM_TAG_RESOURCE,
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/unpack.h b/lib/pengine/unpack.h
index 14beebd7ce..fa57928050 100644
--- a/lib/pengine/unpack.h
+++ b/lib/pengine/unpack.h
@@ -1,94 +1,96 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef PENGINE_UNPACK__H
#define PENGINE_UNPACK__H
extern gboolean unpack_resources(
xmlNode *xml_resources, pe_working_set_t *data_set);
extern gboolean unpack_config(xmlNode *config, pe_working_set_t *data_set);
extern gboolean unpack_nodes(xmlNode *xml_nodes, pe_working_set_t *data_set);
+extern gboolean unpack_domains(xmlNode *xml_domains, pe_working_set_t *data_set);
+
extern gboolean unpack_status(xmlNode *status, pe_working_set_t *data_set);
extern gint sort_op_by_callid(gconstpointer a, gconstpointer b);
extern gboolean unpack_lrm_resources(
node_t *node, xmlNode * lrm_state, pe_working_set_t *data_set);
extern gboolean add_node_attrs(
xmlNode * attrs, node_t *node, gboolean overwrite, pe_working_set_t *data_set);
extern gboolean determine_online_status(
xmlNode * node_state, node_t *this_node, pe_working_set_t *data_set);
extern const char *param_value(
GHashTable *hash, xmlNode * parent, const char *name);
extern char *clone_zero(const char *last_rsc_id);
extern char *increment_clone(char *last_rsc_id);
/*
* The man pages for both curses and ncurses suggest inclusion of "curses.h".
* We believe the following to be acceptable and portable.
*/
#if defined(HAVE_LIBNCURSES) || defined(HAVE_LIBCURSES)
# if defined(HAVE_NCURSES_H) && !defined(HAVE_INCOMPATIBLE_PRINTW)
# include <ncurses.h>
# define CURSES_ENABLED 1
# elif defined(HAVE_NCURSES_NCURSES_H) && !defined(HAVE_INCOMPATIBLE_PRINTW)
# include <ncurses/ncurses.h>
# define CURSES_ENABLED 1
# elif defined(HAVE_CURSES_H) && !defined(HAVE_INCOMPATIBLE_PRINTW)
# include <curses.h>
# define CURSES_ENABLED 1
# elif defined(HAVE_CURSES_CURSES_H) && !defined(HAVE_INCOMPATIBLE_PRINTW)
# include <curses/curses.h>
# define CURSES_ENABLED 1
# else
# define CURSES_ENABLED 0
# endif
#else
# define CURSES_ENABLED 0
#endif
#if CURSES_ENABLED
# define status_printw(fmt, args...) printw(fmt, ##args)
#else
# define status_printw(fmt, args...) \
crm_err("printw support requires ncurses to be available during configure"); \
do_crm_log(LOG_WARNING, fmt, ##args);
#endif
#define status_print(fmt, args...) \
if(options & pe_print_html) { \
FILE *stream = print_data; \
fprintf(stream, fmt, ##args); \
} else if(options & pe_print_ncurses) { \
status_printw(fmt, ##args); \
} else if(options & pe_print_printf) { \
FILE *stream = print_data; \
fprintf(stream, fmt, ##args); \
} else if(options & pe_print_log) { \
int log_level = *(int*)print_data; \
do_crm_log(log_level, fmt, ##args); \
}
#endif
diff --git a/pengine/constraints.c b/pengine/constraints.c
index fc6d41609d..5c45897bba 100644
--- a/pengine/constraints.c
+++ b/pengine/constraints.c
@@ -1,1247 +1,1278 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <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,
};
enum pe_ordering get_flags(
const char *id, enum pe_order_kind kind,
const char *action_first, const char *action_then, gboolean invert);
gboolean
unpack_constraints(xmlNode * xml_constraints, pe_working_set_t *data_set)
{
xmlNode *lifetime = NULL;
xml_child_iter(
xml_constraints, 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_debug_3("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 {
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
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;
}
}
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_debug_2("Upgrade : recovery - implies right");
cons_weight |= pe_order_implies_right;
}
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_debug_2("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;
}
cons_weight = pe_order_optional;
if(kind == pe_order_kind_optional && rsc_then->restart_type == pe_restart_restart) {
crm_debug_2("Upgrade : recovery - implies left");
cons_weight |= pe_order_implies_left;
}
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_debug_2("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;
+ }
- if(node != NULL && score != NULL) {
+ 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) {
- rsc2node_new(id, rsc_lh, score_i, match, data_set);
- return TRUE;
- } else {
+ if(!match) {
return FALSE;
}
- }
-
- xml_child_iter_filter(
+ location = rsc2node_new(id, rsc_lh, score_i, match, data_set);
+
+ } else {
+ xml_child_iter_filter(
xml_obj, rule_xml, XML_TAG_RULE,
empty = FALSE;
crm_debug_2("Unpacking %s/%s", id, ID(rule_xml));
generate_location_rule(rsc_lh, rule_xml, data_set);
);
- if(empty) {
+ if(empty) {
crm_config_err("Invalid location constraint %s:"
" rsc_location must contain at least one rule",
ID(xml_obj));
+ }
}
- return TRUE;
+
+ 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 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_debug_2("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_debug_2("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) {
match_L = node_list_dup(data_set->nodes, TRUE, FALSE);
slist_iter(
node, node_t, match_L, lpc,
node->weight = get_node_score(rule_id, score, raw_score, node);
);
}
slist_iter(
node, node_t, data_set->nodes, lpc,
accept = test_rule(
rule_xml, node->details->attrs, RSC_ROLE_UNKNOWN, data_set->now);
crm_debug_2("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_debug_2("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_debug_5("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_debug_2("No matching nodes for rule %s", rule_id);
return NULL;
}
crm_debug_3("%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_debug_3("%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));
crm_debug_3("Creating ordering constraint %d",
data_set->order_id);
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;
data_set->ordering_constraints = g_list_append(
data_set->ordering_constraints, order);
if(lh_rsc != NULL && rh_rsc != NULL) {
crm_debug_4("Created ordering constraint %d (%s):"
" %s/%s before %s/%s",
order->id, ordering_type2text(order->type),
lh_rsc->id, lh_action_task,
rh_rsc->id, rh_action_task);
} else if(lh_rsc != NULL) {
crm_debug_4("Created ordering constraint %d (%s):"
" %s/%s before action %d (%s)",
order->id, ordering_type2text(order->type),
lh_rsc->id, lh_action_task,
rh_action->id, rh_action_task);
} else if(rh_rsc != NULL) {
crm_debug_4("Created ordering constraint %d (%s):"
" action %d (%s) before %s/%s",
order->id, ordering_type2text(order->type),
lh_action->id, lh_action_task,
rh_rsc->id, rh_action_task);
} else {
crm_debug_4("Created ordering constraint %d (%s):"
" action %d (%s) before action %d (%s)",
order->id, ordering_type2text(order->type),
lh_action->id, lh_action_task,
rh_action->id, rh_action_task);
}
return order->id;
}
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_debug_2("Upgrade %s: implies left", id);
flags |= pe_order_implies_left;
if(safe_str_eq(action_then, RSC_DEMOTE)) {
crm_debug_2("Upgrade %s: demote", id);
flags |= pe_order_demote;
}
} else if(kind == pe_order_kind_mandatory) {
crm_debug_2("Upgrade %s: implies right", id);
flags |= pe_order_implies_right;
if(safe_str_eq(action_first, RSC_START)
|| safe_str_eq(action_first, RSC_PROMOTE)) {
crm_debug_2("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)
{
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);
flags = get_flags(id, local_kind, action, action, FALSE);
xml_child_iter_filter(
set, xml_rsc, XML_TAG_RESOURCE_REF,
resource = pe_find_resource(data_set->resources, ID(xml_rsc));
resources = g_list_append(resources, resource);
);
if(g_list_length(resources) == 1) {
crm_err("Single set: %s", id);
*rsc = resource;
*end = NULL;
*begin = NULL;
*inv_end = NULL;
*inv_begin = NULL;
return TRUE;
}
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);
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_left_printed, data_set);
custom_action_order(resource, crm_strdup(key), NULL, NULL, NULL, *end,
flags|pe_order_implies_right_printed, data_set);
if(local_kind == pe_order_kind_serialize) {
/* Serialize before everything that comes after */
slist_iter(
then_rsc, resource_t, set_iter, lpc,
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;
}
}
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);
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_left_printed, data_set);
custom_action_order(resource, crm_strdup(key), NULL, NULL, NULL, *inv_end,
flags|pe_order_implies_right_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);
crm_free(pseudo_id);
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) {
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");
enum pe_ordering flags = get_flags(id, kind, action_1, action_2, FALSE);
if(crm_is_true(sequential_1)) {
/* get the first one */
xml_child_iter_filter(
set1, xml_rsc, XML_TAG_RESOURCE_REF,
rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc));
break;
);
}
if(crm_is_true(sequential_2)) {
/* get the last one */
const char *rid = NULL;
xml_child_iter_filter(
set2, xml_rsc, XML_TAG_RESOURCE_REF,
rid = ID(xml_rsc);
);
rsc_2 = pe_find_resource(data_set->resources, 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) {
xml_child_iter_filter(
set2, xml_rsc, XML_TAG_RESOURCE_REF,
rsc_2 = pe_find_resource(data_set->resources, ID(xml_rsc));
new_rsc_order(rsc_1, action_1, rsc_2, action_2, flags, data_set);
);
} else if(rsc_2 != NULL) {
xml_child_iter_filter(
set1, xml_rsc, XML_TAG_RESOURCE_REF,
rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc));
new_rsc_order(rsc_1, action_1, rsc_2, action_2, flags, data_set);
);
} else {
xml_child_iter_filter(
set1, xml_rsc, XML_TAG_RESOURCE_REF,
rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc));
xml_child_iter_filter(
set2, xml_rsc_2, XML_TAG_RESOURCE_REF,
rsc_2 = pe_find_resource(data_set->resources, ID(xml_rsc_2));
new_rsc_order(rsc_1, action_1, rsc_2, action_2, flags, data_set);
);
);
}
return TRUE;
}
static char *null_or_opkey(resource_t *rsc, const char *action)
{
if(rsc == NULL) {
return NULL;
}
return generate_op_key(rsc->id, action, 0);
}
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 *last = 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);
if(invert == NULL) {
invert = "true";
}
xml_child_iter_filter(
xml_obj, set, XML_CONS_TAG_RSC_SET,
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;
} 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 */last && order_rsc_sets(id, last, set, kind, data_set) == 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(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)
{
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)) {
xml_child_iter_filter(
set, xml_rsc, XML_TAG_RESOURCE_REF,
resource = pe_find_resource(data_set->resources, ID(xml_rsc));
if(with != NULL) {
crm_debug_2("Colocating %s with %s", resource->id, with->id);
rsc_colocation_new(set_id, NULL, local_score, resource, with, role, role, data_set);
}
with = resource;
);
}
return TRUE;
}
static gboolean colocate_rsc_sets(
const char *id, xmlNode *set1, xmlNode *set2, int score, pe_working_set_t *data_set)
{
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 */
xml_child_iter_filter(
set1, xml_rsc, XML_TAG_RESOURCE_REF,
rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc));
break;
);
}
if(crm_is_true(sequential_2)) {
/* get the last one */
const char *rid = NULL;
xml_child_iter_filter(
set2, xml_rsc, XML_TAG_RESOURCE_REF,
rid = ID(xml_rsc);
);
rsc_2 = pe_find_resource(data_set->resources, 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) {
xml_child_iter_filter(
set2, xml_rsc, XML_TAG_RESOURCE_REF,
rsc_2 = pe_find_resource(data_set->resources, ID(xml_rsc));
rsc_colocation_new(id, NULL, score, rsc_1, rsc_2, role_1, role_2, data_set);
);
} else if(rsc_2 != NULL) {
xml_child_iter_filter(
set1, xml_rsc, XML_TAG_RESOURCE_REF,
rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc));
rsc_colocation_new(id, NULL, score, rsc_1, rsc_2, role_1, role_2, data_set);
);
} else {
xml_child_iter_filter(
set1, xml_rsc, XML_TAG_RESOURCE_REF,
rsc_1 = pe_find_resource(data_set->resources, ID(xml_rsc));
xml_child_iter_filter(
set2, xml_rsc_2, XML_TAG_RESOURCE_REF,
rsc_2 = pe_find_resource(data_set->resources, 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;
}
gboolean
unpack_rsc_colocation(xmlNode *xml_obj, pe_working_set_t *data_set)
{
int score_i = 0;
xmlNode *last = NULL;
gboolean any_sets = FALSE;
const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
if(score) {
score_i = char2score(score);
}
xml_child_iter_filter(
xml_obj, set, XML_CONS_TAG_RSC_SET,
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(any_sets == FALSE) {
return unpack_simple_colocation(xml_obj, data_set);
}
return TRUE;
}
gboolean is_active(rsc_to_node_t *cons)
{
return TRUE;
}
diff --git a/pengine/regression.sh b/pengine/regression.sh
index 443ca33bc8..e826603164 100755
--- a/pengine/regression.sh
+++ b/pengine/regression.sh
@@ -1,360 +1,363 @@
#!/bin/bash
# Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This software is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
core=`dirname $0`
. $core/regression.core.sh
io_dir=$test_home/test10
create_mode="true"
info Generating test outputs for these tests...
# do_test file description
info Done.
echo ""
info Performing the following tests from $io_dir
create_mode="false"
echo ""
do_test simple1 "Offline "
do_test simple2 "Start "
do_test simple3 "Start 2 "
do_test simple4 "Start Failed"
do_test simple6 "Stop Start "
do_test simple7 "Shutdown "
#do_test simple8 "Stonith "
#do_test simple9 "Lower version"
#do_test simple10 "Higher version"
do_test simple11 "Priority (ne)"
do_test simple12 "Priority (eq)"
do_test simple8 "Stickiness"
echo ""
do_test params-0 "Params: No change"
do_test params-1 "Params: Changed"
do_test params-2 "Params: Resource definition"
do_test params-4 "Params: Reload"
do_test novell-251689 "Resource definition change + target_role=stopped"
do_test bug-lf-2106 "Restart all anonymous clone instances after config change"
echo ""
do_test orphan-0 "Orphan ignore"
do_test orphan-1 "Orphan stop"
echo ""
do_test target-0 "Target Role : baseline"
do_test target-1 "Target Role : master"
do_test target-2 "Target Role : invalid"
+echo ""
+do_test domain "Failover domains"
+
echo ""
do_test date-1 "Dates" -t "2005-020"
do_test date-2 "Date Spec - Pass" -t "2005-020T12:30"
do_test date-3 "Date Spec - Fail" -t "2005-020T11:30"
do_test probe-0 "Probe (anon clone)"
do_test probe-1 "Pending Probe"
do_test probe-2 "Correctly re-probe cloned groups"
do_test probe-3 "Probe (pending node)"
do_test probe-4 "Probe (pending node + stopped resource)" --rc 4
do_test standby "Standby"
do_test comments "Comments"
echo ""
do_test rsc_dep1 "Must not "
do_test rsc_dep3 "Must "
do_test rsc_dep5 "Must not 3 "
do_test rsc_dep7 "Must 3 "
do_test rsc_dep10 "Must (but cant)"
do_test rsc_dep2 "Must (running) "
do_test rsc_dep8 "Must (running : alt) "
do_test rsc_dep4 "Must (running + move)"
do_test asymmetric "Asymmetric - require explicit location constraints"
echo ""
do_test order1 "Order start 1 "
do_test order2 "Order start 2 "
do_test order3 "Order stop "
do_test order4 "Order (multiple) "
do_test order5 "Order (move) "
do_test order6 "Order (move w/ restart) "
do_test order7 "Order (manditory) "
do_test order-optional "Order (score=0) "
do_test order-required "Order (score=INFINITY) "
do_test bug-lf-2171 "Prevent group start when clone is stopped"
do_test order-clone "Clone ordering should be able to prevent startup of dependant clones"
do_test order-sets "Ordering for resource sets"
do_test order-serialize "Serialize resources without inhibiting migration"
do_test order-serialize-set "Serialize a set of resources without inhibiting migration"
echo ""
do_test coloc-loop "Colocation - loop"
do_test coloc-many-one "Colocation - many-to-one"
do_test coloc-list "Colocation - many-to-one with list"
do_test coloc-group "Colocation - groups"
do_test coloc-slave-anti "Anti-colocation with slave shouldn't prevent master colocation"
do_test coloc-attr "Colocation based on node attributes"
do_test coloc-negative-group "Negative colocation with a group"
do_test coloc-intra-set "Intra-set colocation"
#echo ""
#do_test agent1 "version: lt (empty)"
#do_test agent2 "version: eq "
#do_test agent3 "version: gt "
echo ""
do_test attrs1 "string: eq (and) "
do_test attrs2 "string: lt / gt (and)"
do_test attrs3 "string: ne (or) "
do_test attrs4 "string: exists "
do_test attrs5 "string: not_exists "
do_test attrs6 "is_dc: true "
do_test attrs7 "is_dc: false "
do_test attrs8 "score_attribute "
echo ""
do_test mon-rsc-1 "Schedule Monitor - start"
do_test mon-rsc-2 "Schedule Monitor - move "
do_test mon-rsc-3 "Schedule Monitor - pending start "
do_test mon-rsc-4 "Schedule Monitor - move/pending start"
echo ""
do_test rec-rsc-0 "Resource Recover - no start "
do_test rec-rsc-1 "Resource Recover - start "
do_test rec-rsc-2 "Resource Recover - monitor "
do_test rec-rsc-3 "Resource Recover - stop - ignore"
do_test rec-rsc-4 "Resource Recover - stop - block "
do_test rec-rsc-5 "Resource Recover - stop - fence "
do_test rec-rsc-6 "Resource Recover - multiple - restart"
do_test rec-rsc-7 "Resource Recover - multiple - stop "
do_test rec-rsc-8 "Resource Recover - multiple - block "
do_test rec-rsc-9 "Resource Recover - group/group"
echo ""
do_test quorum-1 "No quorum - ignore"
do_test quorum-2 "No quorum - freeze"
do_test quorum-3 "No quorum - stop "
do_test quorum-4 "No quorum - start anyway"
do_test quorum-5 "No quorum - start anyway (group)"
do_test quorum-6 "No quorum - start anyway (clone)"
echo ""
do_test rec-node-1 "Node Recover - Startup - no fence"
do_test rec-node-2 "Node Recover - Startup - fence "
do_test rec-node-3 "Node Recover - HA down - no fence"
do_test rec-node-4 "Node Recover - HA down - fence "
do_test rec-node-5 "Node Recover - CRM down - no fence"
do_test rec-node-6 "Node Recover - CRM down - fence "
do_test rec-node-7 "Node Recover - no quorum - ignore "
do_test rec-node-8 "Node Recover - no quorum - freeze "
do_test rec-node-9 "Node Recover - no quorum - stop "
do_test rec-node-10 "Node Recover - no quorum - stop w/fence"
do_test rec-node-11 "Node Recover - CRM down w/ group - fence "
do_test rec-node-12 "Node Recover - nothing active - fence "
do_test rec-node-13 "Node Recover - failed resource + shutdown - fence "
do_test rec-node-15 "Node Recover - unknown lrm section"
do_test rec-node-14 "Serialize all stonith's"
echo ""
do_test multi1 "Multiple Active (stop/start)"
echo ""
do_test migrate-stop "Migration in a stopping stack"
do_test migrate-start "Migration in a starting stack"
do_test migrate-stop_start "Migration in a restarting stack"
do_test migrate-stop-complex "Migration in a complex stopping stack"
do_test migrate-start-complex "Migration in a complex starting stack"
do_test migrate-stop-start-complex "Migration in a complex moving stack"
do_test migrate-1 "Migrate (migrate)"
do_test migrate-2 "Migrate (stable)"
do_test migrate-3 "Migrate (failed migrate_to)"
do_test migrate-4 "Migrate (failed migrate_from)"
do_test novell-252693 "Migration in a stopping stack"
do_test novell-252693-2 "Migration in a starting stack"
do_test novell-252693-3 "Non-Migration in a starting and stopping stack"
do_test bug-1820 "Migration in a group"
do_test bug-1820-1 "Non-migration in a group"
do_test migrate-5 "Primitive migration with a clone"
#echo ""
#do_test complex1 "Complex "
echo ""
do_test group1 "Group "
do_test group2 "Group + Native "
do_test group3 "Group + Group "
do_test group4 "Group + Native (nothing)"
do_test group5 "Group + Native (move) "
do_test group6 "Group + Group (move) "
do_test group7 "Group colocation"
do_test group13 "Group colocation (cant run)"
do_test group8 "Group anti-colocation"
do_test group9 "Group recovery"
do_test group10 "Group partial recovery"
do_test group11 "Group target_role"
do_test group14 "Group stop (graph terminated)"
do_test group15 "-ve group colocation"
do_test bug-1573 "Partial stop of a group with two children"
do_test bug-1718 "Mandatory group ordering - Stop group_FUN"
echo ""
do_test clone-anon-probe-1 "Probe the correct (anonymous) clone instance for each node"
do_test clone-anon-probe-2 "Avoid needless re-probing of anonymous clones"
do_test clone-anon-failcount "Merge failcounts for anonymous clones"
do_test inc0 "Incarnation start"
do_test inc1 "Incarnation start order"
do_test inc2 "Incarnation silent restart, stop, move"
do_test inc3 "Inter-incarnation ordering, silent restart, stop, move"
do_test inc4 "Inter-incarnation ordering, silent restart, stop, move (ordered)"
do_test inc5 "Inter-incarnation ordering, silent restart, stop, move (restart 1)"
do_test inc6 "Inter-incarnation ordering, silent restart, stop, move (restart 2)"
do_test inc7 "Clone colocation"
do_test inc8 "Clone anti-colocation"
do_test inc9 "Non-unique clone"
do_test inc10 "Non-unique clone (stop)"
do_test inc11 "Primitive colocation with clones"
do_test inc12 "Clone shutdown"
do_test cloned-group "Make sure only the correct number of cloned groups are started"
do_test clone-no-shuffle "Dont prioritize allocation of instances that must be moved"
do_test clone-max-zero "Orphan processing with clone-max=0"
do_test clone-anon-dup "Bug LF#2087 - Correctly parse the state of anonymous clones that are active more than once per node"
do_test bug-lf-2160 "Dont shuffle clones due to colocation"
do_test bug-lf-2213 "clone-node-max enforcement for cloned groups"
do_test bug-lf-2153 "Clone ordering constraints"
do_test bug-lf-2361 "Ensure clones observe mandatory ordering constraints if the LHS is unrunnable"
do_test bug-lf-2317 "Avoid needless restart of primitive depending on a clone"
do_test clone-colocate-instance-1 "Colocation with a specific clone instance (negative example)"
do_test clone-colocate-instance-2 "Colocation with a specific clone instance"
do_test clone-order-instance "Ordering with specific clone instances"
echo ""
do_test master-0 "Stopped -> Slave"
do_test master-1 "Stopped -> Promote"
do_test master-2 "Stopped -> Promote : notify"
do_test master-3 "Stopped -> Promote : master location"
do_test master-4 "Started -> Promote : master location"
do_test master-5 "Promoted -> Promoted"
do_test master-6 "Promoted -> Promoted (2)"
do_test master-7 "Promoted -> Fenced"
do_test master-8 "Promoted -> Fenced -> Moved"
do_test master-9 "Stopped + Promotable + No quorum"
do_test master-10 "Stopped -> Promotable : notify with monitor"
do_test master-11 "Stopped -> Promote : colocation"
do_test novell-239082 "Demote/Promote ordering"
do_test novell-239087 "Stable master placement"
do_test master-12 "Promotion based solely on rsc_location constraints"
do_test master-13 "Include preferences of colocated resources when placing master"
do_test master-demote "Ordering when actions depends on demoting a slave resource"
do_test master-ordering "Prevent resources from starting that need a master"
do_test bug-1765 "Master-Master Colocation (dont stop the slaves)"
do_test master-group "Promotion of cloned groups"
do_test bug-lf-1852 "Don't shuffle master/slave instances unnecessarily"
do_test master-failed-demote "Dont retry failed demote actions"
do_test master-failed-demote-2 "Dont retry failed demote actions (notify=false)"
do_test master-depend "Ensure resources that depend on the master don't get allocated until the master does"
do_test master-reattach "Re-attach to a running master"
do_test master-allow-start "Don't include master score if it would prevent allocation"
do_test master-colocation "Allow master instances placemaker to be influenced by colocation constraints"
do_test master-pseudo "Make sure promote/demote pseudo actions are created correctly"
do_test master-role "Prevent target-role from promoting more than master-max instances"
do_test bug-lf-2358 "Master-Master anti-colocation"
echo ""
do_test managed-0 "Managed (reference)"
do_test managed-1 "Not managed - down "
do_test managed-2 "Not managed - up "
echo ""
do_test interleave-0 "Interleave (reference)"
do_test interleave-1 "coloc - not interleaved"
do_test interleave-2 "coloc - interleaved "
do_test interleave-3 "coloc - interleaved (2)"
do_test interleave-pseudo-stop "Interleaved clone during stonith"
do_test interleave-stop "Interleaved clone during stop"
do_test interleave-restart "Interleaved clone during dependancy restart"
echo ""
do_test notify-0 "Notify reference"
do_test notify-1 "Notify simple"
do_test notify-2 "Notify simple, confirm"
do_test notify-3 "Notify move, confirm"
do_test novell-239079 "Notification priority"
#do_test notify-2 "Notify - 764"
echo ""
do_test 594 "OSDL #594"
do_test 662 "OSDL #662"
do_test 696 "OSDL #696"
do_test 726 "OSDL #726"
do_test 735 "OSDL #735"
do_test 764 "OSDL #764"
do_test 797 "OSDL #797"
do_test 829 "OSDL #829"
do_test 994 "OSDL #994"
do_test 994-2 "OSDL #994 - with a dependant resource"
do_test 1360 "OSDL #1360 - Clone stickiness"
do_test 1484 "OSDL #1484 - on_fail=stop"
do_test 1494 "OSDL #1494 - Clone stability"
do_test unrunnable-1 "Unrunnable"
do_test stonith-0 "Stonith loop - 1"
do_test stonith-1 "Stonith loop - 2"
do_test stonith-2 "Stonith loop - 3"
do_test stonith-3 "Stonith startup"
do_test bug-1572-1 "Recovery of groups depending on master/slave"
do_test bug-1572-2 "Recovery of groups depending on master/slave when the master is never re-promoted"
do_test bug-1685 "Depends-on-master ordering"
do_test bug-1822 "Dont promote partially active groups"
do_test bug-pm-11 "New resource added to a m/s group"
do_test bug-pm-12 "Recover only the failed portion of a cloned group"
do_test bug-n-387749 "Don't shuffle clone instances"
do_test bug-n-385265 "Don't ignore the failure stickiness of group children - resource_idvscommon should stay stopped"
do_test bug-n-385265-2 "Ensure groups are migrated instead of remaining partially active on the current node"
do_test bug-lf-1920 "Correctly handle probes that find active resources"
do_test bnc-515172 "Location constraint with multiple expressions"
do_test colocate-primitive-with-clone "Optional colocation with a clone"
do_test use-after-free-merge "Use-after-free in native_merge_weights"
echo ""
do_test systemhealth1 "System Health () #1"
do_test systemhealth2 "System Health () #2"
do_test systemhealth3 "System Health () #3"
do_test systemhealthn1 "System Health (None) #1"
do_test systemhealthn2 "System Health (None) #2"
do_test systemhealthn3 "System Health (None) #3"
do_test systemhealthm1 "System Health (Migrate On Red) #1"
do_test systemhealthm2 "System Health (Migrate On Red) #2"
do_test systemhealthm3 "System Health (Migrate On Red) #3"
do_test systemhealtho1 "System Health (Only Green) #1"
do_test systemhealtho2 "System Health (Only Green) #2"
do_test systemhealtho3 "System Health (Only Green) #3"
do_test systemhealthp1 "System Health (Progessive) #1"
do_test systemhealthp2 "System Health (Progessive) #2"
do_test systemhealthp3 "System Health (Progessive) #3"
echo ""
do_test utilization "Placement Strategy - utilization"
do_test minimal "Placement Strategy - minimal"
do_test balanced "Placement Strategy - balanced"
echo ""
do_test utilization-order1 "Utilization Order - Simple"
do_test utilization-order2 "Utilization Order - Complex"
do_test utilization-order3 "Utilization Order - Migrate"
echo ""
test_results
diff --git a/pengine/test10/domain.dot b/pengine/test10/domain.dot
new file mode 100644
index 0000000000..b80581ccae
--- /dev/null
+++ b/pengine/test10/domain.dot
@@ -0,0 +1,21 @@
+digraph "g" {
+"Dummy_monitor_0 puma1" -> "probe_complete puma1" [ style = bold]
+"Dummy_monitor_0 puma1" [ style=bold color="green" fontcolor="black" ]
+"Dummy_monitor_0 puma2" -> "probe_complete puma2" [ style = bold]
+"Dummy_monitor_0 puma2" [ style=bold color="green" fontcolor="black" ]
+"Dummy_monitor_0 puma3" -> "probe_complete puma3" [ style = bold]
+"Dummy_monitor_0 puma3" [ style=bold color="green" fontcolor="black" ]
+"Dummy_monitor_0 puma4" -> "probe_complete puma4" [ style = bold]
+"Dummy_monitor_0 puma4" [ style=bold color="green" fontcolor="black" ]
+"Dummy_start_0 puma2" [ style=bold color="green" fontcolor="black" ]
+"probe_complete puma1" -> "probe_complete" [ style = bold]
+"probe_complete puma1" [ style=bold color="green" fontcolor="black" ]
+"probe_complete puma2" -> "probe_complete" [ style = bold]
+"probe_complete puma2" [ style=bold color="green" fontcolor="black" ]
+"probe_complete puma3" -> "probe_complete" [ style = bold]
+"probe_complete puma3" [ style=bold color="green" fontcolor="black" ]
+"probe_complete puma4" -> "probe_complete" [ style = bold]
+"probe_complete puma4" [ style=bold color="green" fontcolor="black" ]
+"probe_complete" -> "Dummy_start_0 puma2" [ style = bold]
+"probe_complete" [ style=bold color="green" fontcolor="orange" ]
+}
diff --git a/pengine/test10/domain.exp b/pengine/test10/domain.exp
new file mode 100644
index 0000000000..53faad1f19
--- /dev/null
+++ b/pengine/test10/domain.exp
@@ -0,0 +1,121 @@
+<transition_graph cluster-delay="60s" stonith-timeout="60s" failed-stop-offset="INFINITY" failed-start-offset="INFINITY" batch-limit="30" transition_id="0">
+ <synapse id="0">
+ <action_set>
+ <rsc_op id="4" operation="monitor" operation_key="Dummy_monitor_0" on_node="puma1" on_node_uuid="puma1">
+ <primitive id="Dummy" long-id="Dummy" class="ocf" provider="heartbeat" type="Dummy"/>
+ <attributes CRM_meta_op_target_rc="7" CRM_meta_timeout="20000" crm_feature_set="3.0.2"/>
+ </rsc_op>
+ </action_set>
+ <inputs/>
+ </synapse>
+ <synapse id="1">
+ <action_set>
+ <rsc_op id="6" operation="monitor" operation_key="Dummy_monitor_0" on_node="puma2" on_node_uuid="puma2">
+ <primitive id="Dummy" long-id="Dummy" class="ocf" provider="heartbeat" type="Dummy"/>
+ <attributes CRM_meta_op_target_rc="7" CRM_meta_timeout="20000" crm_feature_set="3.0.2"/>
+ </rsc_op>
+ </action_set>
+ <inputs/>
+ </synapse>
+ <synapse id="2">
+ <action_set>
+ <rsc_op id="8" operation="monitor" operation_key="Dummy_monitor_0" on_node="puma3" on_node_uuid="puma3">
+ <primitive id="Dummy" long-id="Dummy" class="ocf" provider="heartbeat" type="Dummy"/>
+ <attributes CRM_meta_op_target_rc="7" CRM_meta_timeout="20000" crm_feature_set="3.0.2"/>
+ </rsc_op>
+ </action_set>
+ <inputs/>
+ </synapse>
+ <synapse id="3">
+ <action_set>
+ <rsc_op id="10" operation="monitor" operation_key="Dummy_monitor_0" on_node="puma4" on_node_uuid="puma4">
+ <primitive id="Dummy" long-id="Dummy" class="ocf" provider="heartbeat" type="Dummy"/>
+ <attributes CRM_meta_op_target_rc="7" CRM_meta_timeout="20000" crm_feature_set="3.0.2"/>
+ </rsc_op>
+ </action_set>
+ <inputs/>
+ </synapse>
+ <synapse id="4">
+ <action_set>
+ <rsc_op id="11" operation="start" operation_key="Dummy_start_0" on_node="puma2" on_node_uuid="puma2">
+ <primitive id="Dummy" long-id="Dummy" class="ocf" provider="heartbeat" type="Dummy"/>
+ <attributes CRM_meta_timeout="20000" crm_feature_set="3.0.2"/>
+ </rsc_op>
+ </action_set>
+ <inputs>
+ <trigger>
+ <pseudo_event id="2" operation="probe_complete" operation_key="probe_complete"/>
+ </trigger>
+ </inputs>
+ </synapse>
+ <synapse id="5">
+ <action_set>
+ <pseudo_event id="2" operation="probe_complete" operation_key="probe_complete">
+ <attributes crm_feature_set="3.0.2"/>
+ </pseudo_event>
+ </action_set>
+ <inputs>
+ <trigger>
+ <rsc_op id="3" operation="probe_complete" operation_key="probe_complete" on_node="puma1" on_node_uuid="puma1"/>
+ </trigger>
+ <trigger>
+ <rsc_op id="5" operation="probe_complete" operation_key="probe_complete" on_node="puma2" on_node_uuid="puma2"/>
+ </trigger>
+ <trigger>
+ <rsc_op id="7" operation="probe_complete" operation_key="probe_complete" on_node="puma3" on_node_uuid="puma3"/>
+ </trigger>
+ <trigger>
+ <rsc_op id="9" operation="probe_complete" operation_key="probe_complete" on_node="puma4" on_node_uuid="puma4"/>
+ </trigger>
+ </inputs>
+ </synapse>
+ <synapse id="6" priority="1000000">
+ <action_set>
+ <rsc_op id="3" operation="probe_complete" operation_key="probe_complete" on_node="puma1" on_node_uuid="puma1">
+ <attributes CRM_meta_op_no_wait="true" crm_feature_set="3.0.2"/>
+ </rsc_op>
+ </action_set>
+ <inputs>
+ <trigger>
+ <rsc_op id="4" operation="monitor" operation_key="Dummy_monitor_0" on_node="puma1" on_node_uuid="puma1"/>
+ </trigger>
+ </inputs>
+ </synapse>
+ <synapse id="7" priority="1000000">
+ <action_set>
+ <rsc_op id="5" operation="probe_complete" operation_key="probe_complete" on_node="puma2" on_node_uuid="puma2">
+ <attributes CRM_meta_op_no_wait="true" crm_feature_set="3.0.2"/>
+ </rsc_op>
+ </action_set>
+ <inputs>
+ <trigger>
+ <rsc_op id="6" operation="monitor" operation_key="Dummy_monitor_0" on_node="puma2" on_node_uuid="puma2"/>
+ </trigger>
+ </inputs>
+ </synapse>
+ <synapse id="8" priority="1000000">
+ <action_set>
+ <rsc_op id="7" operation="probe_complete" operation_key="probe_complete" on_node="puma3" on_node_uuid="puma3">
+ <attributes CRM_meta_op_no_wait="true" crm_feature_set="3.0.2"/>
+ </rsc_op>
+ </action_set>
+ <inputs>
+ <trigger>
+ <rsc_op id="8" operation="monitor" operation_key="Dummy_monitor_0" on_node="puma3" on_node_uuid="puma3"/>
+ </trigger>
+ </inputs>
+ </synapse>
+ <synapse id="9" priority="1000000">
+ <action_set>
+ <rsc_op id="9" operation="probe_complete" operation_key="probe_complete" on_node="puma4" on_node_uuid="puma4">
+ <attributes CRM_meta_op_no_wait="true" crm_feature_set="3.0.2"/>
+ </rsc_op>
+ </action_set>
+ <inputs>
+ <trigger>
+ <rsc_op id="10" operation="monitor" operation_key="Dummy_monitor_0" on_node="puma4" on_node_uuid="puma4"/>
+ </trigger>
+ </inputs>
+ </synapse>
+</transition_graph>
+
diff --git a/pengine/test10/domain.scores b/pengine/test10/domain.scores
new file mode 100644
index 0000000000..9e1b01cf8e
--- /dev/null
+++ b/pengine/test10/domain.scores
@@ -0,0 +1,5 @@
+Allocation scores:
+native_color: Dummy allocation score on puma1: 100
+native_color: Dummy allocation score on puma2: 2000
+native_color: Dummy allocation score on puma3: -INFINITY
+native_color: Dummy allocation score on puma4: 0
diff --git a/pengine/test10/domain.xml b/pengine/test10/domain.xml
new file mode 100644
index 0000000000..c8b6c55622
--- /dev/null
+++ b/pengine/test10/domain.xml
@@ -0,0 +1,40 @@
+<cib crm_feature_set="2.1" admin_epoch="0" epoch="9698" num_updates="6" dc-uuid="c1b88737-8c7c-4824-81de-843fe7f2e88a" have-quorum="1" remote-tls-port="0" validate-with="pacemaker-1.1" cib-last-written="Fri Apr 30 17:14:33 2010">
+ <configuration>
+ <crm_config>
+ <cluster_property_set id="cib-bootstrap-options">
+ <nvpair id="nvpair.id21897" name="no-quorum-policy" value="ignore"/>
+ <nvpair id="nvpair.id21906" name="stonith-enabled" value="false"/>
+ </cluster_property_set>
+ </crm_config>
+ <nodes>
+ <node id="puma1" uname="puma1" type="normal"/>
+ <node id="puma2" uname="puma2" type="normal"/>
+ <node id="puma3" uname="puma3" type="normal"/>
+ <node id="puma4" uname="puma4" type="normal"/>
+ </nodes>
+ <domains>
+ <domain id="dom1">
+ <node name="puma1" score="100"/>
+ <node name="puma2" score="2000"/>
+ <node name="puma3" score="-INFINITY"/>
+ </domain>
+ </domains>
+ <rsc_defaults>
+ <meta_attributes id="foo">
+ <nvpair id="nvpair.id21908" name="failure-timeout" value="10"/>
+ </meta_attributes>
+ </rsc_defaults>
+ <resources>
+ <primitive id="Dummy" class="ocf" provider="heartbeat" type="Dummy"/>
+ </resources>
+ <constraints>
+ <rsc_location id="l-1" rsc="Dummy" domain="dom1"/>
+ </constraints>
+ </configuration>
+ <status>
+ <node_state id="puma1" uname="puma1" ha="active" in_ccm="true" crmd="online" join="member" expected="member" shutdown="0"/>
+ <node_state id="puma2" uname="puma2" ha="active" in_ccm="true" crmd="online" join="member" expected="member" shutdown="0"/>
+ <node_state id="puma3" uname="puma3" ha="active" in_ccm="true" crmd="online" join="member" expected="member" shutdown="0"/>
+ <node_state id="puma4" uname="puma4" ha="active" in_ccm="true" crmd="online" join="member" expected="member" shutdown="0"/>
+ </status>
+</cib>
diff --git a/xml/constraints-1.1.rng b/xml/constraints-1.1.rng
index f493cf4951..44ab338959 100644
--- a/xml/constraints-1.1.rng
+++ b/xml/constraints-1.1.rng
@@ -1,192 +1,202 @@
<?xml version="1.0" encoding="utf-8"?>
<grammar xmlns="http://relaxng.org/ns/structure/1.0"
datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
<start>
<ref name="element-constraints"/>
</start>
<define name="element-constraints">
<zeroOrMore>
<choice>
<ref name="element-location"/>
<ref name="element-colocation"/>
<ref name="element-order"/>
</choice>
</zeroOrMore>
</define>
<define name="element-location">
<element name="rsc_location">
<attribute name="id"><data type="ID"/></attribute>
<attribute name="rsc"><data type="IDREF"/></attribute>
<choice>
<group>
- <externalRef href="score.rng"/>
- <attribute name="node"><text/></attribute>
+ <optional>
+ <attribute name="role">
+ <ref name="attribute-roles"/>
+ </attribute>
+ </optional>
+ <choice>
+ <attribute name="domain"><data type="IDREF"/></attribute>
+ <group>
+ <attribute name="node"><text/></attribute>
+ <externalRef href="score.rng"/>
+ </group>
+ </choice>
</group>
<oneOrMore>
<externalRef href="rule.rng"/>
</oneOrMore>
</choice>
<optional>
<ref name="element-lifetime"/>
</optional>
</element>
</define>
<define name="element-resource-set">
<element name="resource_set">
<choice>
<attribute name="id-ref"><data type="IDREF"/></attribute>
<group>
<attribute name="id"><data type="ID"/></attribute>
<optional>
<attribute name="sequential"><data type="boolean"/></attribute>
</optional>
<optional>
<attribute name="action">
<ref name="attribute-actions"/>
</attribute>
</optional>
<optional>
<attribute name="role">
<ref name="attribute-roles"/>
</attribute>
</optional>
<optional>
<externalRef href="score.rng"/>
</optional>
<oneOrMore>
<element name="resource_ref">
<attribute name="id"><data type="IDREF"/></attribute>
</element>
</oneOrMore>
</group>
</choice>
</element>
</define>
<define name="element-colocation">
<element name="rsc_colocation">
<attribute name="id"><data type="ID"/></attribute>
<optional>
<choice>
<externalRef href="score.rng"/>
<attribute name="score-attribute"><text/></attribute>
<attribute name="score-attribute-mangle"><text/></attribute>
</choice>
</optional>
<optional>
<ref name="element-lifetime"/>
</optional>
<choice>
<oneOrMore>
<ref name="element-resource-set"/>
</oneOrMore>
<group>
<attribute name="rsc"><data type="IDREF"/></attribute>
<attribute name="with-rsc"><data type="IDREF"/></attribute>
<optional>
<attribute name="node-attribute"><text/></attribute>
</optional>
<optional>
<attribute name="rsc-role">
<ref name="attribute-roles"/>
</attribute>
</optional>
<optional>
<attribute name="with-rsc-role">
<ref name="attribute-roles"/>
</attribute>
</optional>
<optional>
<attribute name="rsc-instance"><data type="integer"/></attribute>
</optional>
<optional>
<attribute name="with-rsc-instance"><data type="integer"/></attribute>
</optional>
</group>
</choice>
</element>
</define>
<define name="element-order">
<element name="rsc_order">
<attribute name="id"><data type="ID"/></attribute>
<optional>
<ref name="element-lifetime"/>
</optional>
<optional>
<attribute name="symmetrical"><data type="boolean"/></attribute>
</optional>
<optional>
<choice>
<externalRef href="score.rng"/>
<attribute name="kind">
<ref name="order-types"/>
</attribute>
</choice>
</optional>
<choice>
<oneOrMore>
<ref name="element-resource-set"/>
</oneOrMore>
<group>
<attribute name="first"><data type="IDREF"/></attribute>
<attribute name="then"><data type="IDREF"/></attribute>
<optional>
<attribute name="first-action">
<ref name="attribute-actions"/>
</attribute>
</optional>
<optional>
<attribute name="then-action">
<ref name="attribute-actions"/>
</attribute>
</optional>
<optional>
<attribute name="first-instance"><data type="integer"/></attribute>
</optional>
<optional>
<attribute name="then-instance"><data type="integer"/></attribute>
</optional>
</group>
</choice>
</element>
</define>
<define name="attribute-actions">
<choice>
<value>start</value>
<value>promote</value>
<value>demote</value>
<value>stop</value>
</choice>
</define>
<define name="attribute-roles">
<choice>
<value>Stopped</value>
<value>Started</value>
<value>Master</value>
<value>Slave</value>
</choice>
</define>
<define name="order-types">
<choice>
<value>Optional</value>
<value>Mandatory</value>
<value>Serialize</value>
</choice>
</define>
<define name="element-lifetime">
<element name="lifetime">
<oneOrMore>
<externalRef href="rule.rng"/>
</oneOrMore>
</element>
</define>
</grammar>
diff --git a/xml/pacemaker-1.1.rng b/xml/pacemaker-1.1.rng
index 70a34ea1f9..5ce6ec40e7 100644
--- a/xml/pacemaker-1.1.rng
+++ b/xml/pacemaker-1.1.rng
@@ -1,126 +1,141 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- types: http://www.w3.org/TR/xmlschema-2/#dateTime -->
<grammar xmlns="http://relaxng.org/ns/structure/1.0"
datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
<start>
<element name="cib">
<ref name="element-cib"/>
</element>
</start>
<define name="element-cib">
<ref name="attribute-options"/>
<element name="configuration">
<interleave>
<element name="crm_config">
<zeroOrMore>
<element name="cluster_property_set">
<externalRef href="nvset.rng"/>
</element>
</zeroOrMore>
</element>
<optional>
<element name="rsc_defaults">
<zeroOrMore>
<element name="meta_attributes">
<externalRef href="nvset.rng"/>
</element>
</zeroOrMore>
</element>
</optional>
<optional>
<element name="op_defaults">
<zeroOrMore>
<element name="meta_attributes">
<externalRef href="nvset.rng"/>
</element>
</zeroOrMore>
</element>
</optional>
<ref name="element-nodes"/>
<element name="resources">
<externalRef href="resources-1.1.rng"/>
</element>
+ <optional>
+ <element name="domains">
+ <zeroOrMore>
+ <element name="domain">
+ <attribute name="id"><data type="ID"/></attribute>
+ <zeroOrMore>
+ <element name="node">
+ <attribute name="name"><text/></attribute>
+ <externalRef href="score.rng"/>
+ </element>
+ </zeroOrMore>
+ </element>
+ </zeroOrMore>
+ </element>
+ </optional>
<element name="constraints">
<externalRef href="constraints-1.1.rng"/>
</element>
</interleave>
</element>
<element name="status">
<ref name="element-status"/>
</element>
</define>
<define name="attribute-options">
<externalRef href="versions.rng"/>
<optional>
<attribute name="crm_feature_set"><text/></attribute>
</optional>
<optional>
<attribute name="remote-tls-port"><data type="nonNegativeInteger"/></attribute>
</optional>
<optional>
<attribute name="remote-clear-port"><data type="nonNegativeInteger"/></attribute>
</optional>
<optional>
<attribute name="have-quorum"><data type="boolean"/></attribute>
</optional>
<optional>
<attribute name="dc-uuid"><text/></attribute>
</optional>
<optional>
<attribute name="cib-last-written"><text/></attribute>
</optional>
<optional>
<attribute name="no-quorum-panic"><data type="boolean"/></attribute>
</optional>
</define>
<define name="element-nodes">
<element name="nodes">
<zeroOrMore>
<element name="node">
<attribute name="id"><text/></attribute>
<attribute name="uname"><text/></attribute>
<attribute name="type">
<choice>
<value>normal</value>
<value>member</value>
<value>ping</value>
</choice>
</attribute>
<optional>
<attribute name="description"><text/></attribute>
</optional>
<zeroOrMore>
<choice>
<element name="instance_attributes">
<externalRef href="nvset.rng"/>
</element>
<element name="utilization">
<externalRef href="nvset.rng"/>
</element>
</choice>
</zeroOrMore>
</element>
</zeroOrMore>
</element>
</define>
<define name="element-status">
<zeroOrMore>
<choice>
<attribute>
<anyName/>
<text/>
</attribute>
<element>
<anyName/>
<ref name="element-status"/>
</element>
<text/>
</choice>
</zeroOrMore>
</define>
</grammar>

File Metadata

Mime Type
text/x-diff
Expires
Sat, Jan 25, 6:37 AM (1 d, 7 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1321535
Default Alt Text
(179 KB)

Event Timeline