Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F2825289
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
116 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/include/crm/msg_xml.h b/include/crm/msg_xml.h
index 114efd3302..be7f3e53bf 100644
--- a/include/crm/msg_xml.h
+++ b/include/crm/msg_xml.h
@@ -1,295 +1,296 @@
/*
* Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#ifndef PCMK__CRM_MSG_XML__H
# define PCMK__CRM_MSG_XML__H
# include <crm/common/xml.h>
#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
#include <crm/msg_xml_compat.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* This file defines constants for various XML syntax (mainly element and
* attribute names).
*
* For consistency, new constants should start with "PCMK_", followed by:
* * "XE" for XML element names
* * "XA" for XML attribute names
* * "OPT" for cluster option (property) names
* * "META" for meta-attribute names
* * "VALUE" for enumerated values for various options
*
* Old names that don't follow this policy should eventually be deprecated and
* replaced with names that do.
*
* Symbols should be public if the user may specify them somewhere (especially
* the CIB) or if they're part of a well-defined structure that a user may need
* to parse. They should be internal if they're used only internally to
* Pacemaker (such as daemon IPC/CPG message XML).
*
* Constants belong in the following locations:
* * Public "XE" and "XA": msg_xml.h
* * Internal "XE" and "XA": crm_internal.h
* * Public "OPT", "META", and "VALUE": options.h
* * Internal "OPT", "META", and "VALUE": options_internal.h
*
* For meta-attributes that can be specified as either XML attributes or nvpair
* names, use "META" unless using both "XA" and "META" constants adds clarity.
* An example is operation attributes, which can be specified either as
* attributes of the PCMK_XE_OP element or as nvpairs in a meta-attribute set
* beneath the PCMK_XE_OP element.
*/
/*
* XML elements
*/
#define PCMK_XE_ACL_GROUP "acl_group"
#define PCMK_XE_ACL_PERMISSION "acl_permission"
#define PCMK_XE_ACL_ROLE "acl_role"
#define PCMK_XE_ACL_TARGET "acl_target"
#define PCMK_XE_ACLS "acls"
#define PCMK_XE_ACTION "action"
#define PCMK_XE_ACTIONS "actions"
#define PCMK_XE_ALERT "alert"
#define PCMK_XE_ALERTS "alerts"
#define PCMK_XE_ATTRIBUTE "attribute"
#define PCMK_XE_BUNDLE "bundle"
#define PCMK_XE_CHANGE "change"
#define PCMK_XE_CHANGE_ATTR "change-attr"
#define PCMK_XE_CHANGE_LIST "change-list"
#define PCMK_XE_CHANGE_RESULT "change-result"
#define PCMK_XE_CIB "cib"
#define PCMK_XE_CLONE "clone"
#define PCMK_XE_CLUSTER_PROPERTY_SET "cluster_property_set"
#define PCMK_XE_CONFIGURATION "configuration"
#define PCMK_XE_CONSTRAINTS "constraints"
#define PCMK_XE_CONTENT "content"
#define PCMK_XE_CRM_CONFIG "crm_config"
#define PCMK_XE_DATE_EXPRESSION "date_expression"
#define PCMK_XE_DATE_SPEC "date_spec"
#define PCMK_XE_DIFF "diff"
#define PCMK_XE_DURATION "duration"
#define PCMK_XE_EXPRESSION "expression"
#define PCMK_XE_FENCING_LEVEL "fencing-level"
#define PCMK_XE_FENCING_TOPOLOGY "fencing-topology"
#define PCMK_XE_GROUP "group"
#define PCMK_XE_INSTANCE_ATTRIBUTES "instance_attributes"
#define PCMK_XE_LONGDESC "longdesc"
#define PCMK_XE_META_ATTRIBUTES "meta_attributes"
#define PCMK_XE_NETWORK "network"
#define PCMK_XE_NODE "node"
#define PCMK_XE_NODES "nodes"
#define PCMK_XE_NVPAIR "nvpair"
#define PCMK_XE_OBJ_REF "obj_ref"
#define PCMK_XE_OP "op"
#define PCMK_XE_OP_DEFAULTS "op_defaults"
#define PCMK_XE_OPERATION "operation"
#define PCMK_XE_OP_EXPRESSION "op_expression"
#define PCMK_XE_OPTION "option"
#define PCMK_XE_PARAMETER "parameter"
#define PCMK_XE_PARAMETERS "parameters"
#define PCMK_XE_PORT_MAPPING "port-mapping"
#define PCMK_XE_POSITION "position"
#define PCMK_XE_PRIMITIVE "primitive"
#define PCMK_XE_RECIPIENT "recipient"
#define PCMK_XE_RESOURCE "resource"
#define PCMK_XE_RESOURCE_AGENT "resource-agent"
#define PCMK_XE_RESOURCE_REF "resource_ref"
#define PCMK_XE_RESOURCE_SET "resource_set"
#define PCMK_XE_RESOURCES "resources"
#define PCMK_XE_ROLE "role"
#define PCMK_XE_RULE "rule"
#define PCMK_XE_RSC_COLOCATION "rsc_colocation"
#define PCMK_XE_RSC_DEFAULTS "rsc_defaults"
#define PCMK_XE_RSC_EXPRESSION "rsc_expression"
#define PCMK_XE_RSC_LOCATION "rsc_location"
#define PCMK_XE_RSC_ORDER "rsc_order"
#define PCMK_XE_RSC_TICKET "rsc_ticket"
#define PCMK_XE_SELECT "select"
#define PCMK_XE_SELECT_ATTRIBUTES "select_attributes"
#define PCMK_XE_SELECT_FENCING "select_fencing"
#define PCMK_XE_SELECT_NODES "select_nodes"
#define PCMK_XE_SELECT_RESOURCES "select_resources"
#define PCMK_XE_SHORTDESC "shortdesc"
#define PCMK_XE_SOURCE "source"
#define PCMK_XE_STATUS "status"
#define PCMK_XE_STORAGE "storage"
#define PCMK_XE_STORAGE_MAPPING "storage-mapping"
#define PCMK_XE_TAG "tag"
#define PCMK_XE_TAGS "tags"
#define PCMK_XE_TARGET "target"
#define PCMK_XE_TEMPLATE "template"
#define PCMK_XE_TICKET "ticket"
#define PCMK_XE_TICKETS "tickets"
#define PCMK_XE_UTILIZATION "utilization"
#define PCMK_XE_VERSION "version"
/*
* XML attributes
*/
#define PCMK_XA_ACTION "action"
#define PCMK_XA_ADD_HOST "add-host"
#define PCMK_XA_ADMIN_EPOCH "admin_epoch"
#define PCMK_XA_ATTRIBUTE "attribute"
#define PCMK_XA_BOOLEAN_OP "boolean-op"
#define PCMK_XA_CIB_LAST_WRITTEN "cib-last-written"
#define PCMK_XA_CLASS "class"
#define PCMK_XA_CLIENT "client"
#define PCMK_XA_COMPLETED "completed"
#define PCMK_XA_CONTROL_PORT "control-port"
#define PCMK_XA_CRM_DEBUG_ORIGIN "crm-debug-origin"
#define PCMK_XA_CRM_FEATURE_SET "crm_feature_set"
#define PCMK_XA_CRM_TIMESTAMP "crm-timestamp"
#define PCMK_XA_DAYS "days"
#define PCMK_XA_DC_UUID "dc-uuid"
#define PCMK_XA_DEFAULT "default"
#define PCMK_XA_DELEGATE "delegate"
#define PCMK_XA_DESCRIPTION "description"
#define PCMK_XA_DEVICES "devices"
#define PCMK_XA_DISABLED "disabled"
#define PCMK_XA_DURATION "duration"
#define PCMK_XA_END "end"
#define PCMK_XA_EPOCH "epoch"
#define PCMK_XA_EXEC_TIME "exec-time"
#define PCMK_XA_EXECUTION_DATE "execution-date"
#define PCMK_XA_EXIT_REASON "exit-reason"
#define PCMK_XA_EXPECTED_UP "expected_up"
#define PCMK_XA_EXTENDED_STATUS "extended-status"
#define PCMK_XA_FAILED "failed"
#define PCMK_XA_FAILURE_IGNORED "failure_ignored"
#define PCMK_XA_FEATURE_SET "feature_set"
#define PCMK_XA_FIRST "first"
#define PCMK_XA_FIRST_ACTION "first-action"
#define PCMK_XA_FORMAT "format"
#define PCMK_XA_HAVE_QUORUM "have-quorum"
#define PCMK_XA_HEALTH "health"
#define PCMK_XA_HOST "host"
#define PCMK_XA_HOST_INTERFACE "host-interface"
#define PCMK_XA_HOST_NETMASK "host-netmask"
#define PCMK_XA_HOURS "hours"
#define PCMK_XA_ID "id"
#define PCMK_XA_ID_REF "id-ref"
#define PCMK_XA_IMAGE "image"
#define PCMK_XA_INDEX "index"
#define PCMK_XA_INFLUENCE "influence"
#define PCMK_XA_INTERNAL_PORT "internal-port"
#define PCMK_XA_IP_RANGE_START "ip-range-start"
#define PCMK_XA_IS_DC "is_dc"
#define PCMK_XA_KIND "kind"
#define PCMK_XA_LANG "lang"
#define PCMK_XA_LAST_GRANTED "last-granted"
#define PCMK_XA_LAST_RC_CHANGE "last-rc-change"
#define PCMK_XA_LOSS_POLICY "loss-policy"
#define PCMK_XA_MAINTENANCE "maintenance"
#define PCMK_XA_MANAGED "managed"
#define PCMK_XA_MINUTES "minutes"
#define PCMK_XA_MIXED_VERSION "mixed_version"
#define PCMK_XA_MONTHDAYS "monthdays"
#define PCMK_XA_MONTHS "months"
#define PCMK_XA_MULTI_STATE "multi_state"
#define PCMK_XA_NAME "name"
#define PCMK_XA_NETWORK "network"
#define PCMK_XA_NO_QUORUM_PANIC "no-quorum-panic"
#define PCMK_XA_NODE "node"
#define PCMK_XA_NODE_ATTRIBUTE "node-attribute"
#define PCMK_XA_NUM_UPDATES "num_updates"
#define PCMK_XA_NUMBER "number"
#define PCMK_XA_OBJECT_TYPE "object-type"
#define PCMK_XA_ONLINE "online"
#define PCMK_XA_OP "op"
#define PCMK_XA_OPERATION "operation"
#define PCMK_XA_OPTIONS "options"
#define PCMK_XA_ORIGIN "origin"
#define PCMK_XA_PATH "path"
#define PCMK_XA_PENDING "pending"
#define PCMK_XA_PORT "port"
#define PCMK_XA_PRESENT "present"
#define PCMK_XA_PROMOTED_MAX "promoted-max"
#define PCMK_XA_PROVIDER "provider"
#define PCMK_XA_QUEUE_TIME "queue-time"
#define PCMK_XA_RANGE "range"
#define PCMK_XA_REASON "reason"
#define PCMK_XA_REFERENCE "reference"
#define PCMK_XA_REMOTE_CLEAR_PORT "remote-clear-port"
#define PCMK_XA_REMOTE_TLS_PORT "remote-tls-port"
#define PCMK_XA_REPLICAS "replicas"
#define PCMK_XA_REPLICAS_PER_HOST "replicas-per-host"
#define PCMK_XA_REQUEST "request"
#define PCMK_XA_REQUIRE_ALL "require-all"
#define PCMK_XA_RESOURCE "resource"
#define PCMK_XA_RESOURCE_DISCOVERY "resource-discovery"
#define PCMK_XA_RESOURCES_RUNNING "resources_running"
#define PCMK_XA_RESULT "result"
#define PCMK_XA_ROLE "role"
#define PCMK_XA_RSC "rsc"
#define PCMK_XA_RSC_PATTERN "rsc-pattern"
#define PCMK_XA_RSC_ROLE "rsc-role"
#define PCMK_XA_RUN_COMMAND "run-command"
#define PCMK_XA_RUNNING "running"
#define PCMK_XA_SCOPE "scope"
#define PCMK_XA_SCORE "score"
#define PCMK_XA_SCORE_ATTRIBUTE "score-attribute"
#define PCMK_XA_SEQUENTIAL "sequential"
#define PCMK_XA_SECONDS "seconds"
#define PCMK_XA_SHUTDOWN "shutdown"
#define PCMK_XA_SOURCE_DIR "source-dir"
#define PCMK_XA_SOURCE_DIR_ROOT "source-dir-root"
#define PCMK_XA_STANDBY "standby"
#define PCMK_XA_STANDBY_ONFAIL "standby_onfail"
#define PCMK_XA_START "start"
#define PCMK_XA_STATUS "status"
#define PCMK_XA_SYMMETRICAL "symmetrical"
#define PCMK_XA_TARGET "target"
#define PCMK_XA_TARGET_ATTRIBUTE "target-attribute"
#define PCMK_XA_TARGET_DIR "target-dir"
#define PCMK_XA_TARGET_PATTERN "target-pattern"
+#define PCMK_XA_TARGET_ROLE "target_role"
#define PCMK_XA_TARGET_VALUE "target-value"
#define PCMK_XA_TEMPLATE "template"
#define PCMK_XA_TICKET "ticket"
#define PCMK_XA_TIME "time"
#define PCMK_XA_THEN "then"
#define PCMK_XA_THEN_ACTION "then-action"
#define PCMK_XA_TYPE "type"
#define PCMK_XA_UNAME "uname"
#define PCMK_XA_UNCLEAN "unclean"
#define PCMK_XA_UNIQUE "unique"
#define PCMK_XA_UPDATE_CLIENT "update-client"
#define PCMK_XA_UPDATE_ORIGIN "update-origin"
#define PCMK_XA_UPDATE_USER "update-user"
#define PCMK_XA_USER "user"
#define PCMK_XA_VALIDATE_WITH "validate-with"
#define PCMK_XA_VALUE "value"
#define PCMK_XA_VALUE_SOURCE "value-source"
#define PCMK_XA_VERSION "version"
#define PCMK_XA_WEEKDAYS "weekdays"
#define PCMK_XA_WEEKS "weeks"
#define PCMK_XA_WEEKYEARS "weekyears"
#define PCMK_XA_WITH_QUORUM "with_quorum"
#define PCMK_XA_WITH_RSC "with-rsc"
#define PCMK_XA_WITH_RSC_ROLE "with-rsc-role"
#define PCMK_XA_XPATH "xpath"
#define PCMK_XA_YEARDAYS "yeardays"
#define PCMK_XA_YEARS "years"
# define ID(x) crm_element_value(x, PCMK_XA_ID)
#ifdef __cplusplus
}
#endif
#endif
diff --git a/lib/pengine/clone.c b/lib/pengine/clone.c
index 129e5fb437..83e66baf60 100644
--- a/lib/pengine/clone.c
+++ b/lib/pengine/clone.c
@@ -1,1540 +1,1537 @@
/*
* Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#include <crm_internal.h>
#include <stdint.h>
#include <crm/pengine/rules.h>
#include <crm/pengine/status.h>
#include <crm/pengine/internal.h>
#include <pe_status_private.h>
#include <crm/msg_xml.h>
#include <crm/common/output.h>
#include <crm/common/xml_internal.h>
#include <crm/common/scheduler_internal.h>
#ifdef PCMK__COMPAT_2_0
#define PROMOTED_INSTANCES PCMK__ROLE_PROMOTED_LEGACY "s"
#define UNPROMOTED_INSTANCES PCMK__ROLE_UNPROMOTED_LEGACY "s"
#else
#define PROMOTED_INSTANCES PCMK__ROLE_PROMOTED
#define UNPROMOTED_INSTANCES PCMK__ROLE_UNPROMOTED
#endif
typedef struct clone_variant_data_s {
int clone_max;
int clone_node_max;
int promoted_max;
int promoted_node_max;
int total_clones;
uint32_t flags; // Group of enum pcmk__clone_flags
notify_data_t *stop_notify;
notify_data_t *start_notify;
notify_data_t *demote_notify;
notify_data_t *promote_notify;
xmlNode *xml_obj_child;
} clone_variant_data_t;
#define get_clone_variant_data(data, rsc) \
CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_clone)); \
data = (clone_variant_data_t *) rsc->variant_opaque;
/*!
* \internal
* \brief Return the maximum number of clone instances allowed to be run
*
* \param[in] clone Clone or clone instance to check
*
* \return Maximum instances for \p clone
*/
int
pe__clone_max(const pcmk_resource_t *clone)
{
const clone_variant_data_t *clone_data = NULL;
get_clone_variant_data(clone_data, pe__const_top_resource(clone, false));
return clone_data->clone_max;
}
/*!
* \internal
* \brief Return the maximum number of clone instances allowed per node
*
* \param[in] clone Promotable clone or clone instance to check
*
* \return Maximum allowed instances per node for \p clone
*/
int
pe__clone_node_max(const pcmk_resource_t *clone)
{
const clone_variant_data_t *clone_data = NULL;
get_clone_variant_data(clone_data, pe__const_top_resource(clone, false));
return clone_data->clone_node_max;
}
/*!
* \internal
* \brief Return the maximum number of clone instances allowed to be promoted
*
* \param[in] clone Promotable clone or clone instance to check
*
* \return Maximum promoted instances for \p clone
*/
int
pe__clone_promoted_max(const pcmk_resource_t *clone)
{
clone_variant_data_t *clone_data = NULL;
get_clone_variant_data(clone_data, pe__const_top_resource(clone, false));
return clone_data->promoted_max;
}
/*!
* \internal
* \brief Return the maximum number of clone instances allowed to be promoted
*
* \param[in] clone Promotable clone or clone instance to check
*
* \return Maximum promoted instances for \p clone
*/
int
pe__clone_promoted_node_max(const pcmk_resource_t *clone)
{
clone_variant_data_t *clone_data = NULL;
get_clone_variant_data(clone_data, pe__const_top_resource(clone, false));
return clone_data->promoted_node_max;
}
static GList *
sorted_hash_table_values(GHashTable *table)
{
GList *retval = NULL;
GHashTableIter iter;
gpointer key, value;
g_hash_table_iter_init(&iter, table);
while (g_hash_table_iter_next(&iter, &key, &value)) {
if (!g_list_find_custom(retval, value, (GCompareFunc) strcmp)) {
retval = g_list_prepend(retval, (char *) value);
}
}
retval = g_list_sort(retval, (GCompareFunc) strcmp);
return retval;
}
static GList *
nodes_with_status(GHashTable *table, const char *status)
{
GList *retval = NULL;
GHashTableIter iter;
gpointer key, value;
g_hash_table_iter_init(&iter, table);
while (g_hash_table_iter_next(&iter, &key, &value)) {
if (!strcmp((char *) value, status)) {
retval = g_list_prepend(retval, key);
}
}
retval = g_list_sort(retval, (GCompareFunc) pcmk__numeric_strcasecmp);
return retval;
}
static GString *
node_list_to_str(const GList *list)
{
GString *retval = NULL;
for (const GList *iter = list; iter != NULL; iter = iter->next) {
pcmk__add_word(&retval, 1024, (const char *) iter->data);
}
return retval;
}
static void
clone_header(pcmk__output_t *out, int *rc, const pcmk_resource_t *rsc,
clone_variant_data_t *clone_data, const char *desc)
{
GString *attrs = NULL;
if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) {
pcmk__add_separated_word(&attrs, 64, "promotable", ", ");
}
if (pcmk_is_set(rsc->flags, pcmk_rsc_unique)) {
pcmk__add_separated_word(&attrs, 64, "unique", ", ");
}
if (pe__resource_is_disabled(rsc)) {
pcmk__add_separated_word(&attrs, 64, "disabled", ", ");
}
if (pcmk_is_set(rsc->flags, pcmk_rsc_maintenance)) {
pcmk__add_separated_word(&attrs, 64, "maintenance", ", ");
} else if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
pcmk__add_separated_word(&attrs, 64, "unmanaged", ", ");
}
if (attrs != NULL) {
PCMK__OUTPUT_LIST_HEADER(out, FALSE, *rc, "Clone Set: %s [%s] (%s)%s%s%s",
rsc->id, ID(clone_data->xml_obj_child),
(const char *) attrs->str, desc ? " (" : "",
desc ? desc : "", desc ? ")" : "");
g_string_free(attrs, TRUE);
} else {
PCMK__OUTPUT_LIST_HEADER(out, FALSE, *rc, "Clone Set: %s [%s]%s%s%s",
rsc->id, ID(clone_data->xml_obj_child),
desc ? " (" : "", desc ? desc : "",
desc ? ")" : "");
}
}
void
pe__force_anon(const char *standard, pcmk_resource_t *rsc, const char *rid,
pcmk_scheduler_t *scheduler)
{
if (pe_rsc_is_clone(rsc)) {
clone_variant_data_t *clone_data = rsc->variant_opaque;
pcmk__config_warn("Ignoring " PCMK_META_GLOBALLY_UNIQUE " for %s "
"because %s resources such as %s can be used only as "
"anonymous clones", rsc->id, standard, rid);
clone_data->clone_node_max = 1;
clone_data->clone_max = QB_MIN(clone_data->clone_max,
g_list_length(scheduler->nodes));
}
}
pcmk_resource_t *
find_clone_instance(const pcmk_resource_t *rsc, const char *sub_id)
{
char *child_id = NULL;
pcmk_resource_t *child = NULL;
const char *child_base = NULL;
clone_variant_data_t *clone_data = NULL;
get_clone_variant_data(clone_data, rsc);
child_base = ID(clone_data->xml_obj_child);
child_id = crm_strdup_printf("%s:%s", child_base, sub_id);
child = pe_find_resource(rsc->children, child_id);
free(child_id);
return child;
}
pcmk_resource_t *
pe__create_clone_child(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler)
{
gboolean as_orphan = FALSE;
char *inc_num = NULL;
char *inc_max = NULL;
pcmk_resource_t *child_rsc = NULL;
xmlNode *child_copy = NULL;
clone_variant_data_t *clone_data = NULL;
get_clone_variant_data(clone_data, rsc);
CRM_CHECK(clone_data->xml_obj_child != NULL, return FALSE);
if (clone_data->total_clones >= clone_data->clone_max) {
// If we've already used all available instances, this is an orphan
as_orphan = TRUE;
}
// Allocate instance numbers in numerical order (starting at 0)
inc_num = pcmk__itoa(clone_data->total_clones);
inc_max = pcmk__itoa(clone_data->clone_max);
child_copy = copy_xml(clone_data->xml_obj_child);
crm_xml_add(child_copy, PCMK__META_CLONE, inc_num);
if (pe__unpack_resource(child_copy, &child_rsc, rsc,
scheduler) != pcmk_rc_ok) {
goto bail;
}
/* child_rsc->globally_unique = rsc->globally_unique; */
CRM_ASSERT(child_rsc);
clone_data->total_clones += 1;
pcmk__rsc_trace(child_rsc, "Setting clone attributes for: %s",
child_rsc->id);
rsc->children = g_list_append(rsc->children, child_rsc);
if (as_orphan) {
pe__set_resource_flags_recursive(child_rsc, pcmk_rsc_removed);
}
add_hash_param(child_rsc->meta, PCMK_META_CLONE_MAX, inc_max);
pcmk__rsc_trace(rsc, "Added %s instance %s", rsc->id, child_rsc->id);
bail:
free(inc_num);
free(inc_max);
return child_rsc;
}
/*!
* \internal
* \brief Unpack a nonnegative integer value from a resource meta-attribute
*
* \param[in] rsc Resource with meta-attribute
* \param[in] meta_name Name of meta-attribute to unpack
* \param[in] deprecated_name If not NULL, try unpacking this
* if \p meta_name is unset
* \param[in] default_value Value to use if unset
*
* \return Integer parsed from resource's specified meta-attribute if a valid
* nonnegative integer, \p default_value if unset, or 0 if invalid
*/
static int
unpack_meta_int(const pcmk_resource_t *rsc, const char *meta_name,
const char *deprecated_name, int default_value)
{
int integer = default_value;
const char *value = g_hash_table_lookup(rsc->meta, meta_name);
if ((value == NULL) && (deprecated_name != NULL)) {
value = g_hash_table_lookup(rsc->meta, deprecated_name);
}
if (value != NULL) {
pcmk__scan_min_int(value, &integer, 0);
}
return integer;
}
gboolean
clone_unpack(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler)
{
int lpc = 0;
xmlNode *a_child = NULL;
xmlNode *xml_obj = rsc->xml;
clone_variant_data_t *clone_data = NULL;
pcmk__rsc_trace(rsc, "Processing resource %s...", rsc->id);
clone_data = calloc(1, sizeof(clone_variant_data_t));
rsc->variant_opaque = clone_data;
if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) {
// Use 1 as default but 0 for minimum and invalid
// @COMPAT PCMK__META_PROMOTED_MAX_LEGACY deprecated since 2.0.0
clone_data->promoted_max =
unpack_meta_int(rsc, PCMK_META_PROMOTED_MAX,
PCMK__META_PROMOTED_MAX_LEGACY, 1);
// Use 1 as default but 0 for minimum and invalid
// @COMPAT PCMK__META_PROMOTED_NODE_MAX_LEGACY deprecated since 2.0.0
clone_data->promoted_node_max =
unpack_meta_int(rsc, PCMK_META_PROMOTED_NODE_MAX,
PCMK__META_PROMOTED_NODE_MAX_LEGACY, 1);
}
// Implied by calloc()
/* clone_data->xml_obj_child = NULL; */
// Use 1 as default but 0 for minimum and invalid
clone_data->clone_node_max = unpack_meta_int(rsc, PCMK_META_CLONE_NODE_MAX,
NULL, 1);
/* Use number of nodes (but always at least 1, which is handy for crm_verify
* for a CIB without nodes) as default, but 0 for minimum and invalid
*/
clone_data->clone_max = unpack_meta_int(rsc, PCMK_META_CLONE_MAX, NULL,
QB_MAX(1, g_list_length(scheduler->nodes)));
if (crm_is_true(g_hash_table_lookup(rsc->meta, PCMK_META_ORDERED))) {
clone_data->flags = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE,
"Clone", rsc->id,
clone_data->flags,
pcmk__clone_ordered,
"pcmk__clone_ordered");
}
if (!pcmk_is_set(rsc->flags, pcmk_rsc_unique)
&& (clone_data->clone_node_max > 1)) {
pcmk__config_err("Ignoring " PCMK_META_CLONE_NODE_MAX " of %d for %s "
"because anonymous clones support only one instance "
"per node", clone_data->clone_node_max, rsc->id);
clone_data->clone_node_max = 1;
}
pcmk__rsc_trace(rsc, "Options for %s", rsc->id);
pcmk__rsc_trace(rsc, "\tClone max: %d", clone_data->clone_max);
pcmk__rsc_trace(rsc, "\tClone node max: %d", clone_data->clone_node_max);
pcmk__rsc_trace(rsc, "\tClone is unique: %s",
pcmk__flag_text(rsc->flags, pcmk_rsc_unique));
pcmk__rsc_trace(rsc, "\tClone is promotable: %s",
pcmk__flag_text(rsc->flags, pcmk_rsc_promotable));
// Clones may contain a single group or primitive
for (a_child = pcmk__xe_first_child(xml_obj); a_child != NULL;
a_child = pcmk__xe_next(a_child)) {
if (pcmk__str_any_of((const char *) a_child->name,
PCMK_XE_PRIMITIVE, PCMK_XE_GROUP, NULL)) {
clone_data->xml_obj_child = a_child;
break;
}
}
if (clone_data->xml_obj_child == NULL) {
pcmk__config_err("%s has nothing to clone", rsc->id);
return FALSE;
}
/*
* Make clones ever so slightly sticky by default
*
* This helps ensure clone instances are not shuffled around the cluster
* for no benefit in situations when pre-allocation is not appropriate
*/
if (g_hash_table_lookup(rsc->meta, PCMK_META_RESOURCE_STICKINESS) == NULL) {
add_hash_param(rsc->meta, PCMK_META_RESOURCE_STICKINESS, "1");
}
/* This ensures that the PCMK_META_GLOBALLY_UNIQUE value always exists for
* children to inherit when being unpacked, as well as in resource agents'
* environment.
*/
add_hash_param(rsc->meta, PCMK_META_GLOBALLY_UNIQUE,
pcmk__flag_text(rsc->flags, pcmk_rsc_unique));
if (clone_data->clone_max <= 0) {
/* Create one child instance so that unpack_find_resource() will hook up
* any orphans up to the parent correctly.
*/
if (pe__create_clone_child(rsc, scheduler) == NULL) {
return FALSE;
}
} else {
// Create a child instance for each available instance number
for (lpc = 0; lpc < clone_data->clone_max; lpc++) {
if (pe__create_clone_child(rsc, scheduler) == NULL) {
return FALSE;
}
}
}
pcmk__rsc_trace(rsc, "Added %d children to resource %s...",
clone_data->clone_max, rsc->id);
return TRUE;
}
gboolean
clone_active(pcmk_resource_t * rsc, gboolean all)
{
GList *gIter = rsc->children;
for (; gIter != NULL; gIter = gIter->next) {
pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
gboolean child_active = child_rsc->fns->active(child_rsc, all);
if (all == FALSE && child_active) {
return TRUE;
} else if (all && child_active == FALSE) {
return FALSE;
}
}
if (all) {
return TRUE;
} else {
return FALSE;
}
}
/*!
* \internal
* \deprecated This function will be removed in a future release
*/
static void
short_print(const char *list, const char *prefix, const char *type,
const char *suffix, long options, void *print_data)
{
if(suffix == NULL) {
suffix = "";
}
if (!pcmk__str_empty(list)) {
if (options & pe_print_html) {
status_print("<li>");
}
status_print("%s%s: [ %s ]%s", prefix, type, list, suffix);
if (options & pe_print_html) {
status_print("</li>\n");
} else if (options & pe_print_suppres_nl) {
/* nothing */
} else if ((options & pe_print_printf) || (options & pe_print_ncurses)) {
status_print("\n");
}
}
}
static const char *
configured_role_str(pcmk_resource_t * rsc)
{
const char *target_role = g_hash_table_lookup(rsc->meta,
PCMK_META_TARGET_ROLE);
if ((target_role == NULL) && rsc->children && rsc->children->data) {
pcmk_resource_t *instance = rsc->children->data; // Any instance will do
target_role = g_hash_table_lookup(instance->meta,
PCMK_META_TARGET_ROLE);
}
return target_role;
}
static enum rsc_role_e
configured_role(pcmk_resource_t *rsc)
{
enum rsc_role_e role = pcmk_role_unknown;
const char *target_role = configured_role_str(rsc);
if (target_role != NULL) {
role = pcmk_parse_role(target_role);
if (role == pcmk_role_unknown) {
pcmk__config_err("Invalid " PCMK_META_TARGET_ROLE
" for resource %s", rsc->id);
}
}
return role;
}
/*!
* \internal
* \deprecated This function will be removed in a future release
*/
static void
clone_print_xml(pcmk_resource_t *rsc, const char *pre_text, long options,
void *print_data)
{
char *child_text = crm_strdup_printf("%s ", pre_text);
const char *target_role = configured_role_str(rsc);
GList *gIter = rsc->children;
status_print("%s<clone ", pre_text);
status_print(PCMK_XA_ID "=\"%s\" ", rsc->id);
status_print("multi_state=\"%s\" ",
pcmk__flag_text(rsc->flags, pcmk_rsc_promotable));
status_print("unique=\"%s\" ",
pcmk__flag_text(rsc->flags, pcmk_rsc_unique));
status_print("managed=\"%s\" ",
pcmk__flag_text(rsc->flags, pcmk_rsc_managed));
status_print("failed=\"%s\" ",
pcmk__flag_text(rsc->flags, pcmk_rsc_failed));
status_print("failure_ignored=\"%s\" ",
pcmk__flag_text(rsc->flags, pcmk_rsc_ignore_failure));
if (target_role) {
status_print("target_role=\"%s\" ", target_role);
}
status_print(">\n");
for (; gIter != NULL; gIter = gIter->next) {
pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
child_rsc->fns->print(child_rsc, child_text, options, print_data);
}
status_print("%s</clone>\n", pre_text);
free(child_text);
}
bool
is_set_recursive(const pcmk_resource_t *rsc, long long flag, bool any)
{
GList *gIter;
bool all = !any;
if (pcmk_is_set(rsc->flags, flag)) {
if(any) {
return TRUE;
}
} else if(all) {
return FALSE;
}
for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
if(is_set_recursive(gIter->data, flag, any)) {
if(any) {
return TRUE;
}
} else if(all) {
return FALSE;
}
}
if(all) {
return TRUE;
}
return FALSE;
}
/*!
* \internal
* \deprecated This function will be removed in a future release
*/
void
clone_print(pcmk_resource_t *rsc, const char *pre_text, long options,
void *print_data)
{
GString *list_text = NULL;
char *child_text = NULL;
GString *stopped_list = NULL;
GList *promoted_list = NULL;
GList *started_list = NULL;
GList *gIter = rsc->children;
clone_variant_data_t *clone_data = NULL;
int active_instances = 0;
if (pre_text == NULL) {
pre_text = " ";
}
if (options & pe_print_xml) {
clone_print_xml(rsc, pre_text, options, print_data);
return;
}
get_clone_variant_data(clone_data, rsc);
child_text = crm_strdup_printf("%s ", pre_text);
status_print("%sClone Set: %s [%s]%s%s%s",
pre_text ? pre_text : "", rsc->id, ID(clone_data->xml_obj_child),
pcmk_is_set(rsc->flags, pcmk_rsc_promotable)? " (promotable)" : "",
pcmk_is_set(rsc->flags, pcmk_rsc_unique)? " (unique)" : "",
pcmk_is_set(rsc->flags, pcmk_rsc_managed)? "" : " (unmanaged)");
if (options & pe_print_html) {
status_print("\n<ul>\n");
} else if ((options & pe_print_log) == 0) {
status_print("\n");
}
for (; gIter != NULL; gIter = gIter->next) {
gboolean print_full = FALSE;
pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
gboolean partially_active = child_rsc->fns->active(child_rsc, FALSE);
if (options & pe_print_clone_details) {
print_full = TRUE;
}
if (pcmk_is_set(rsc->flags, pcmk_rsc_unique)) {
// Print individual instance when unique (except stopped orphans)
if (partially_active
|| !pcmk_is_set(rsc->flags, pcmk_rsc_removed)) {
print_full = TRUE;
}
// Everything else in this block is for anonymous clones
} else if (pcmk_is_set(options, pe_print_pending)
&& (child_rsc->pending_task != NULL)
&& strcmp(child_rsc->pending_task, "probe")) {
// Print individual instance when non-probe action is pending
print_full = TRUE;
} else if (partially_active == FALSE) {
// List stopped instances when requested (except orphans)
if (!pcmk_is_set(child_rsc->flags, pcmk_rsc_removed)
&& !pcmk_is_set(options, pe_print_clone_active)) {
pcmk__add_word(&stopped_list, 1024, child_rsc->id);
}
} else if (is_set_recursive(child_rsc, pcmk_rsc_removed, TRUE)
|| !is_set_recursive(child_rsc, pcmk_rsc_managed, FALSE)
|| is_set_recursive(child_rsc, pcmk_rsc_failed, TRUE)) {
// Print individual instance when active orphaned/unmanaged/failed
print_full = TRUE;
} else if (child_rsc->fns->active(child_rsc, TRUE)) {
// Instance of fully active anonymous clone
pcmk_node_t *location = NULL;
location = child_rsc->fns->location(child_rsc, NULL, TRUE);
if (location) {
// Instance is active on a single node
enum rsc_role_e a_role = child_rsc->fns->state(child_rsc, TRUE);
if (location->details->online == FALSE && location->details->unclean) {
print_full = TRUE;
} else if (a_role > pcmk_role_unpromoted) {
promoted_list = g_list_append(promoted_list, location);
} else {
started_list = g_list_append(started_list, location);
}
} else {
/* uncolocated group - bleh */
print_full = TRUE;
}
} else {
// Instance of partially active anonymous clone
print_full = TRUE;
}
if (print_full) {
if (options & pe_print_html) {
status_print("<li>\n");
}
child_rsc->fns->print(child_rsc, child_text, options, print_data);
if (options & pe_print_html) {
status_print("</li>\n");
}
}
}
/* Promoted */
promoted_list = g_list_sort(promoted_list, pe__cmp_node_name);
for (gIter = promoted_list; gIter; gIter = gIter->next) {
pcmk_node_t *host = gIter->data;
pcmk__add_word(&list_text, 1024, host->details->uname);
active_instances++;
}
if (list_text != NULL) {
short_print((const char *) list_text->str, child_text,
PROMOTED_INSTANCES, NULL, options, print_data);
g_string_truncate(list_text, 0);
}
g_list_free(promoted_list);
/* Started/Unpromoted */
started_list = g_list_sort(started_list, pe__cmp_node_name);
for (gIter = started_list; gIter; gIter = gIter->next) {
pcmk_node_t *host = gIter->data;
pcmk__add_word(&list_text, 1024, host->details->uname);
active_instances++;
}
if (list_text != NULL) {
if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) {
enum rsc_role_e role = configured_role(rsc);
if (role == pcmk_role_unpromoted) {
short_print((const char *) list_text->str, child_text,
UNPROMOTED_INSTANCES " (" PCMK_META_TARGET_ROLE ")",
NULL, options, print_data);
} else {
short_print((const char *) list_text->str, child_text,
UNPROMOTED_INSTANCES, NULL, options, print_data);
}
} else {
short_print((const char *) list_text->str, child_text, "Started",
NULL, options, print_data);
}
}
g_list_free(started_list);
if (!pcmk_is_set(options, pe_print_clone_active)) {
const char *state = "Stopped";
enum rsc_role_e role = configured_role(rsc);
if (role == pcmk_role_stopped) {
state = "Stopped (disabled)";
}
if (!pcmk_is_set(rsc->flags, pcmk_rsc_unique)
&& (clone_data->clone_max > active_instances)) {
GList *nIter;
GList *list = g_hash_table_get_values(rsc->allowed_nodes);
/* Custom stopped list for non-unique clones */
if (stopped_list != NULL) {
g_string_truncate(stopped_list, 0);
}
if (list == NULL) {
/* Clusters with PCMK_OPT_SYMMETRIC_CLUSTER=false haven't
* calculated allowed_nodes yet. If we've not probed for them
* yet, the Stopped list will be empty.
*/
list = g_hash_table_get_values(rsc->known_on);
}
list = g_list_sort(list, pe__cmp_node_name);
for (nIter = list; nIter != NULL; nIter = nIter->next) {
pcmk_node_t *node = (pcmk_node_t *) nIter->data;
if (pe_find_node(rsc->running_on, node->details->uname) == NULL) {
pcmk__add_word(&stopped_list, 1024, node->details->uname);
}
}
g_list_free(list);
}
if (stopped_list != NULL) {
short_print((const char *) stopped_list->str, child_text, state,
NULL, options, print_data);
}
}
if (options & pe_print_html) {
status_print("</ul>\n");
}
if (list_text != NULL) {
g_string_free(list_text, TRUE);
}
if (stopped_list != NULL) {
g_string_free(stopped_list, TRUE);
}
free(child_text);
}
PCMK__OUTPUT_ARGS("clone", "uint32_t", "pcmk_resource_t *", "GList *",
"GList *")
int
pe__clone_xml(pcmk__output_t *out, va_list args)
{
uint32_t show_opts = va_arg(args, uint32_t);
pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
GList *only_node = va_arg(args, GList *);
GList *only_rsc = va_arg(args, GList *);
-
- const char *desc = NULL;
GList *gIter = rsc->children;
GList *all = NULL;
int rc = pcmk_rc_no_output;
gboolean printed_header = FALSE;
gboolean print_everything = TRUE;
-
-
if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
return rc;
}
print_everything = pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) ||
(strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches));
all = g_list_prepend(all, (gpointer) "*");
for (; gIter != NULL; gIter = gIter->next) {
pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
if (pcmk__rsc_filtered_by_node(child_rsc, only_node)) {
continue;
}
if (child_rsc->fns->is_filtered(child_rsc, only_rsc, print_everything)) {
continue;
}
if (!printed_header) {
const char *multi_state = pcmk__flag_text(rsc->flags,
pcmk_rsc_promotable);
const char *unique = pcmk__flag_text(rsc->flags, pcmk_rsc_unique);
const char *maintenance = pcmk__flag_text(rsc->flags,
pcmk_rsc_maintenance);
const char *managed = pcmk__flag_text(rsc->flags, pcmk_rsc_managed);
const char *disabled = pcmk__btoa(pe__resource_is_disabled(rsc));
const char *failed = pcmk__flag_text(rsc->flags, pcmk_rsc_failed);
const char *ignored = pcmk__flag_text(rsc->flags,
pcmk_rsc_ignore_failure);
+ const char *target_role = configured_role_str(rsc);
+ const char *desc = pe__resource_description(rsc, show_opts);
printed_header = TRUE;
- desc = pe__resource_description(rsc, show_opts);
rc = pe__name_and_nvpairs_xml(out, true, PCMK_XE_CLONE, 10,
- PCMK_XA_ID, rsc->id,
- PCMK_XA_MULTI_STATE, multi_state,
- PCMK_XA_UNIQUE, unique,
- PCMK_XA_MAINTENANCE, maintenance,
- PCMK_XA_MANAGED, managed,
- PCMK_XA_DISABLED, disabled,
- PCMK_XA_FAILED, failed,
- PCMK_XA_FAILURE_IGNORED, ignored,
- "target_role", configured_role_str(rsc),
- PCMK_XA_DESCRIPTION, desc);
+ PCMK_XA_ID, rsc->id,
+ PCMK_XA_MULTI_STATE, multi_state,
+ PCMK_XA_UNIQUE, unique,
+ PCMK_XA_MAINTENANCE, maintenance,
+ PCMK_XA_MANAGED, managed,
+ PCMK_XA_DISABLED, disabled,
+ PCMK_XA_FAILED, failed,
+ PCMK_XA_FAILURE_IGNORED, ignored,
+ PCMK_XA_TARGET_ROLE, target_role,
+ PCMK_XA_DESCRIPTION, desc);
CRM_ASSERT(rc == pcmk_rc_ok);
}
out->message(out, crm_map_element_name(child_rsc->xml), show_opts,
child_rsc, only_node, all);
}
if (printed_header) {
pcmk__output_xml_pop_parent(out);
}
g_list_free(all);
return rc;
}
PCMK__OUTPUT_ARGS("clone", "uint32_t", "pcmk_resource_t *", "GList *",
"GList *")
int
pe__clone_default(pcmk__output_t *out, va_list args)
{
uint32_t show_opts = va_arg(args, uint32_t);
pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
GList *only_node = va_arg(args, GList *);
GList *only_rsc = va_arg(args, GList *);
GHashTable *stopped = NULL;
GString *list_text = NULL;
GList *promoted_list = NULL;
GList *started_list = NULL;
GList *gIter = rsc->children;
const char *desc = NULL;
clone_variant_data_t *clone_data = NULL;
int active_instances = 0;
int rc = pcmk_rc_no_output;
gboolean print_everything = TRUE;
desc = pe__resource_description(rsc, show_opts);
get_clone_variant_data(clone_data, rsc);
if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
return rc;
}
print_everything = pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) ||
(strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches));
for (; gIter != NULL; gIter = gIter->next) {
gboolean print_full = FALSE;
pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
gboolean partially_active = child_rsc->fns->active(child_rsc, FALSE);
if (pcmk__rsc_filtered_by_node(child_rsc, only_node)) {
continue;
}
if (child_rsc->fns->is_filtered(child_rsc, only_rsc, print_everything)) {
continue;
}
if (pcmk_is_set(show_opts, pcmk_show_clone_detail)) {
print_full = TRUE;
}
if (pcmk_is_set(rsc->flags, pcmk_rsc_unique)) {
// Print individual instance when unique (except stopped orphans)
if (partially_active
|| !pcmk_is_set(rsc->flags, pcmk_rsc_removed)) {
print_full = TRUE;
}
// Everything else in this block is for anonymous clones
} else if (pcmk_is_set(show_opts, pcmk_show_pending)
&& (child_rsc->pending_task != NULL)
&& strcmp(child_rsc->pending_task, "probe")) {
// Print individual instance when non-probe action is pending
print_full = TRUE;
} else if (partially_active == FALSE) {
// List stopped instances when requested (except orphans)
if (!pcmk_is_set(child_rsc->flags, pcmk_rsc_removed)
&& !pcmk_is_set(show_opts, pcmk_show_clone_detail)
&& pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
if (stopped == NULL) {
stopped = pcmk__strkey_table(free, free);
}
g_hash_table_insert(stopped, strdup(child_rsc->id), strdup("Stopped"));
}
} else if (is_set_recursive(child_rsc, pcmk_rsc_removed, TRUE)
|| !is_set_recursive(child_rsc, pcmk_rsc_managed, FALSE)
|| is_set_recursive(child_rsc, pcmk_rsc_failed, TRUE)) {
// Print individual instance when active orphaned/unmanaged/failed
print_full = TRUE;
} else if (child_rsc->fns->active(child_rsc, TRUE)) {
// Instance of fully active anonymous clone
pcmk_node_t *location = NULL;
location = child_rsc->fns->location(child_rsc, NULL, TRUE);
if (location) {
// Instance is active on a single node
enum rsc_role_e a_role = child_rsc->fns->state(child_rsc, TRUE);
if (location->details->online == FALSE && location->details->unclean) {
print_full = TRUE;
} else if (a_role > pcmk_role_unpromoted) {
promoted_list = g_list_append(promoted_list, location);
} else {
started_list = g_list_append(started_list, location);
}
} else {
/* uncolocated group - bleh */
print_full = TRUE;
}
} else {
// Instance of partially active anonymous clone
print_full = TRUE;
}
if (print_full) {
GList *all = NULL;
clone_header(out, &rc, rsc, clone_data, desc);
/* Print every resource that's a child of this clone. */
all = g_list_prepend(all, (gpointer) "*");
out->message(out, crm_map_element_name(child_rsc->xml), show_opts,
child_rsc, only_node, all);
g_list_free(all);
}
}
if (pcmk_is_set(show_opts, pcmk_show_clone_detail)) {
PCMK__OUTPUT_LIST_FOOTER(out, rc);
return pcmk_rc_ok;
}
/* Promoted */
promoted_list = g_list_sort(promoted_list, pe__cmp_node_name);
for (gIter = promoted_list; gIter; gIter = gIter->next) {
pcmk_node_t *host = gIter->data;
if (!pcmk__str_in_list(host->details->uname, only_node,
pcmk__str_star_matches|pcmk__str_casei)) {
continue;
}
pcmk__add_word(&list_text, 1024, host->details->uname);
active_instances++;
}
g_list_free(promoted_list);
if ((list_text != NULL) && (list_text->len > 0)) {
clone_header(out, &rc, rsc, clone_data, desc);
out->list_item(out, NULL, PROMOTED_INSTANCES ": [ %s ]",
(const char *) list_text->str);
g_string_truncate(list_text, 0);
}
/* Started/Unpromoted */
started_list = g_list_sort(started_list, pe__cmp_node_name);
for (gIter = started_list; gIter; gIter = gIter->next) {
pcmk_node_t *host = gIter->data;
if (!pcmk__str_in_list(host->details->uname, only_node,
pcmk__str_star_matches|pcmk__str_casei)) {
continue;
}
pcmk__add_word(&list_text, 1024, host->details->uname);
active_instances++;
}
g_list_free(started_list);
if ((list_text != NULL) && (list_text->len > 0)) {
clone_header(out, &rc, rsc, clone_data, desc);
if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) {
enum rsc_role_e role = configured_role(rsc);
if (role == pcmk_role_unpromoted) {
out->list_item(out, NULL,
UNPROMOTED_INSTANCES
" (" PCMK_META_TARGET_ROLE "): [ %s ]",
(const char *) list_text->str);
} else {
out->list_item(out, NULL, UNPROMOTED_INSTANCES ": [ %s ]",
(const char *) list_text->str);
}
} else {
out->list_item(out, NULL, "Started: [ %s ]",
(const char *) list_text->str);
}
}
if (list_text != NULL) {
g_string_free(list_text, TRUE);
}
if (pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
if (!pcmk_is_set(rsc->flags, pcmk_rsc_unique)
&& (clone_data->clone_max > active_instances)) {
GList *nIter;
GList *list = g_hash_table_get_values(rsc->allowed_nodes);
/* Custom stopped table for non-unique clones */
if (stopped != NULL) {
g_hash_table_destroy(stopped);
stopped = NULL;
}
if (list == NULL) {
/* Clusters with PCMK_OPT_SYMMETRIC_CLUSTER=false haven't
* calculated allowed_nodes yet. If we've not probed for them
* yet, the Stopped list will be empty.
*/
list = g_hash_table_get_values(rsc->known_on);
}
list = g_list_sort(list, pe__cmp_node_name);
for (nIter = list; nIter != NULL; nIter = nIter->next) {
pcmk_node_t *node = (pcmk_node_t *) nIter->data;
if (pe_find_node(rsc->running_on, node->details->uname) == NULL &&
pcmk__str_in_list(node->details->uname, only_node,
pcmk__str_star_matches|pcmk__str_casei)) {
xmlNode *probe_op = pe__failed_probe_for_rsc(rsc, node->details->uname);
const char *state = "Stopped";
if (configured_role(rsc) == pcmk_role_stopped) {
state = "Stopped (disabled)";
}
if (stopped == NULL) {
stopped = pcmk__strkey_table(free, free);
}
if (probe_op != NULL) {
int rc;
pcmk__scan_min_int(crm_element_value(probe_op,
PCMK__XA_RC_CODE),
&rc, 0);
g_hash_table_insert(stopped, strdup(node->details->uname),
crm_strdup_printf("Stopped (%s)", services_ocf_exitcode_str(rc)));
} else {
g_hash_table_insert(stopped, strdup(node->details->uname),
strdup(state));
}
}
}
g_list_free(list);
}
if (stopped != NULL) {
GList *list = sorted_hash_table_values(stopped);
clone_header(out, &rc, rsc, clone_data, desc);
for (GList *status_iter = list; status_iter != NULL; status_iter = status_iter->next) {
const char *status = status_iter->data;
GList *nodes = nodes_with_status(stopped, status);
GString *nodes_str = node_list_to_str(nodes);
if (nodes_str != NULL) {
if (nodes_str->len > 0) {
out->list_item(out, NULL, "%s: [ %s ]", status,
(const char *) nodes_str->str);
}
g_string_free(nodes_str, TRUE);
}
g_list_free(nodes);
}
g_list_free(list);
g_hash_table_destroy(stopped);
/* If there are no instances of this clone (perhaps because there are no
* nodes configured), simply output the clone header by itself. This can
* come up in PCS testing.
*/
} else if (active_instances == 0) {
clone_header(out, &rc, rsc, clone_data, desc);
PCMK__OUTPUT_LIST_FOOTER(out, rc);
return rc;
}
}
PCMK__OUTPUT_LIST_FOOTER(out, rc);
return rc;
}
void
clone_free(pcmk_resource_t * rsc)
{
clone_variant_data_t *clone_data = NULL;
get_clone_variant_data(clone_data, rsc);
pcmk__rsc_trace(rsc, "Freeing %s", rsc->id);
for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
CRM_ASSERT(child_rsc);
pcmk__rsc_trace(child_rsc, "Freeing child %s", child_rsc->id);
free_xml(child_rsc->xml);
child_rsc->xml = NULL;
/* There could be a saved unexpanded xml */
free_xml(child_rsc->orig_xml);
child_rsc->orig_xml = NULL;
child_rsc->fns->free(child_rsc);
}
g_list_free(rsc->children);
if (clone_data) {
CRM_ASSERT(clone_data->demote_notify == NULL);
CRM_ASSERT(clone_data->stop_notify == NULL);
CRM_ASSERT(clone_data->start_notify == NULL);
CRM_ASSERT(clone_data->promote_notify == NULL);
}
common_free(rsc);
}
enum rsc_role_e
clone_resource_state(const pcmk_resource_t * rsc, gboolean current)
{
enum rsc_role_e clone_role = pcmk_role_unknown;
GList *gIter = rsc->children;
for (; gIter != NULL; gIter = gIter->next) {
pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
enum rsc_role_e a_role = child_rsc->fns->state(child_rsc, current);
if (a_role > clone_role) {
clone_role = a_role;
}
}
pcmk__rsc_trace(rsc, "%s role: %s", rsc->id, pcmk_role_text(clone_role));
return clone_role;
}
/*!
* \internal
* \brief Check whether a clone has an instance for every node
*
* \param[in] rsc Clone to check
* \param[in] scheduler Scheduler data
*/
bool
pe__is_universal_clone(const pcmk_resource_t *rsc,
const pcmk_scheduler_t *scheduler)
{
if (pe_rsc_is_clone(rsc)) {
clone_variant_data_t *clone_data = rsc->variant_opaque;
if (clone_data->clone_max == g_list_length(scheduler->nodes)) {
return TRUE;
}
}
return FALSE;
}
gboolean
pe__clone_is_filtered(const pcmk_resource_t *rsc, GList *only_rsc,
gboolean check_parent)
{
gboolean passes = FALSE;
clone_variant_data_t *clone_data = NULL;
if (pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches)) {
passes = TRUE;
} else {
get_clone_variant_data(clone_data, rsc);
passes = pcmk__str_in_list(ID(clone_data->xml_obj_child), only_rsc, pcmk__str_star_matches);
if (!passes) {
for (const GList *iter = rsc->children;
iter != NULL; iter = iter->next) {
const pcmk_resource_t *child_rsc = NULL;
child_rsc = (const pcmk_resource_t *) iter->data;
if (!child_rsc->fns->is_filtered(child_rsc, only_rsc, FALSE)) {
passes = TRUE;
break;
}
}
}
}
return !passes;
}
const char *
pe__clone_child_id(const pcmk_resource_t *rsc)
{
clone_variant_data_t *clone_data = NULL;
get_clone_variant_data(clone_data, rsc);
return ID(clone_data->xml_obj_child);
}
/*!
* \internal
* \brief Check whether a clone is ordered
*
* \param[in] clone Clone resource to check
*
* \return true if clone is ordered, otherwise false
*/
bool
pe__clone_is_ordered(const pcmk_resource_t *clone)
{
clone_variant_data_t *clone_data = NULL;
get_clone_variant_data(clone_data, clone);
return pcmk_is_set(clone_data->flags, pcmk__clone_ordered);
}
/*!
* \internal
* \brief Set a clone flag
*
* \param[in,out] clone Clone resource to set flag for
* \param[in] flag Clone flag to set
*
* \return Standard Pacemaker return code (either pcmk_rc_ok if flag was not
* already set or pcmk_rc_already if it was)
*/
int
pe__set_clone_flag(pcmk_resource_t *clone, enum pcmk__clone_flags flag)
{
clone_variant_data_t *clone_data = NULL;
get_clone_variant_data(clone_data, clone);
if (pcmk_is_set(clone_data->flags, flag)) {
return pcmk_rc_already;
}
clone_data->flags = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE,
"Clone", clone->id,
clone_data->flags, flag, "flag");
return pcmk_rc_ok;
}
/*!
* \internal
* \brief Check whether a clone flag is set
*
* \param[in] group Clone resource to check
* \param[in] flags Flag or flags to check
*
* \return \c true if all \p flags are set for \p clone, otherwise \c false
*/
bool
pe__clone_flag_is_set(const pcmk_resource_t *clone, uint32_t flags)
{
clone_variant_data_t *clone_data = NULL;
get_clone_variant_data(clone_data, clone);
CRM_ASSERT(clone_data != NULL);
return pcmk_all_flags_set(clone_data->flags, flags);
}
/*!
* \internal
* \brief Create pseudo-actions needed for promotable clones
*
* \param[in,out] clone Promotable clone to create actions for
* \param[in] any_promoting Whether any instances will be promoted
* \param[in] any_demoting Whether any instance will be demoted
*/
void
pe__create_promotable_pseudo_ops(pcmk_resource_t *clone, bool any_promoting,
bool any_demoting)
{
pcmk_action_t *action = NULL;
pcmk_action_t *action_complete = NULL;
clone_variant_data_t *clone_data = NULL;
get_clone_variant_data(clone_data, clone);
// Create a "promote" action for the clone itself
action = pe__new_rsc_pseudo_action(clone, PCMK_ACTION_PROMOTE,
!any_promoting, true);
// Create a "promoted" action for when all promotions are done
action_complete = pe__new_rsc_pseudo_action(clone, PCMK_ACTION_PROMOTED,
!any_promoting, true);
action_complete->priority = INFINITY;
// Create notification pseudo-actions for promotion
if (clone_data->promote_notify == NULL) {
clone_data->promote_notify = pe__action_notif_pseudo_ops(clone,
PCMK_ACTION_PROMOTE,
action,
action_complete);
}
// Create a "demote" action for the clone itself
action = pe__new_rsc_pseudo_action(clone, PCMK_ACTION_DEMOTE,
!any_demoting, true);
// Create a "demoted" action for when all demotions are done
action_complete = pe__new_rsc_pseudo_action(clone, PCMK_ACTION_DEMOTED,
!any_demoting, true);
action_complete->priority = INFINITY;
// Create notification pseudo-actions for demotion
if (clone_data->demote_notify == NULL) {
clone_data->demote_notify = pe__action_notif_pseudo_ops(clone,
PCMK_ACTION_DEMOTE,
action,
action_complete);
if (clone_data->promote_notify != NULL) {
order_actions(clone_data->stop_notify->post_done,
clone_data->promote_notify->pre, pcmk__ar_ordered);
order_actions(clone_data->start_notify->post_done,
clone_data->promote_notify->pre, pcmk__ar_ordered);
order_actions(clone_data->demote_notify->post_done,
clone_data->promote_notify->pre, pcmk__ar_ordered);
order_actions(clone_data->demote_notify->post_done,
clone_data->start_notify->pre, pcmk__ar_ordered);
order_actions(clone_data->demote_notify->post_done,
clone_data->stop_notify->pre, pcmk__ar_ordered);
}
}
}
/*!
* \internal
* \brief Create all notification data and actions for a clone
*
* \param[in,out] clone Clone to create notifications for
*/
void
pe__create_clone_notifications(pcmk_resource_t *clone)
{
clone_variant_data_t *clone_data = NULL;
get_clone_variant_data(clone_data, clone);
pe__create_action_notifications(clone, clone_data->start_notify);
pe__create_action_notifications(clone, clone_data->stop_notify);
pe__create_action_notifications(clone, clone_data->promote_notify);
pe__create_action_notifications(clone, clone_data->demote_notify);
}
/*!
* \internal
* \brief Free all notification data for a clone
*
* \param[in,out] clone Clone to free notification data for
*/
void
pe__free_clone_notification_data(pcmk_resource_t *clone)
{
clone_variant_data_t *clone_data = NULL;
get_clone_variant_data(clone_data, clone);
pe__free_action_notification_data(clone_data->demote_notify);
clone_data->demote_notify = NULL;
pe__free_action_notification_data(clone_data->stop_notify);
clone_data->stop_notify = NULL;
pe__free_action_notification_data(clone_data->start_notify);
clone_data->start_notify = NULL;
pe__free_action_notification_data(clone_data->promote_notify);
clone_data->promote_notify = NULL;
}
/*!
* \internal
* \brief Create pseudo-actions for clone start/stop notifications
*
* \param[in,out] clone Clone to create pseudo-actions for
* \param[in,out] start Start action for \p clone
* \param[in,out] stop Stop action for \p clone
* \param[in,out] started Started action for \p clone
* \param[in,out] stopped Stopped action for \p clone
*/
void
pe__create_clone_notif_pseudo_ops(pcmk_resource_t *clone,
pcmk_action_t *start, pcmk_action_t *started,
pcmk_action_t *stop, pcmk_action_t *stopped)
{
clone_variant_data_t *clone_data = NULL;
get_clone_variant_data(clone_data, clone);
if (clone_data->start_notify == NULL) {
clone_data->start_notify = pe__action_notif_pseudo_ops(clone,
PCMK_ACTION_START,
start, started);
}
if (clone_data->stop_notify == NULL) {
clone_data->stop_notify = pe__action_notif_pseudo_ops(clone,
PCMK_ACTION_STOP,
stop, stopped);
if ((clone_data->start_notify != NULL)
&& (clone_data->stop_notify != NULL)) {
order_actions(clone_data->stop_notify->post_done,
clone_data->start_notify->pre, pcmk__ar_ordered);
}
}
}
/*!
* \internal
* \brief Get maximum clone resource instances per node
*
* \param[in] rsc Clone resource to check
*
* \return Maximum number of \p rsc instances that can be active on one node
*/
unsigned int
pe__clone_max_per_node(const pcmk_resource_t *rsc)
{
const clone_variant_data_t *clone_data = NULL;
get_clone_variant_data(clone_data, rsc);
return clone_data->clone_node_max;
}
diff --git a/lib/pengine/native.c b/lib/pengine/native.c
index fbd74808c6..3d483247be 100644
--- a/lib/pengine/native.c
+++ b/lib/pengine/native.c
@@ -1,1474 +1,1474 @@
/*
* Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#include <crm_internal.h>
#include <stdint.h>
#include <crm/common/output.h>
#include <crm/pengine/rules.h>
#include <crm/pengine/status.h>
#include <crm/pengine/complex.h>
#include <crm/pengine/internal.h>
#include <crm/msg_xml.h>
#include <pe_status_private.h>
#ifdef PCMK__COMPAT_2_0
#define PROVIDER_SEP "::"
#else
#define PROVIDER_SEP ":"
#endif
/*!
* \internal
* \brief Check whether a resource is active on multiple nodes
*/
static bool
is_multiply_active(const pcmk_resource_t *rsc)
{
unsigned int count = 0;
if (rsc->variant == pcmk_rsc_variant_primitive) {
pe__find_active_requires(rsc, &count);
}
return count > 1;
}
static void
native_priority_to_node(pcmk_resource_t *rsc, pcmk_node_t *node,
gboolean failed)
{
int priority = 0;
if ((rsc->priority == 0) || (failed == TRUE)) {
return;
}
if (rsc->role == pcmk_role_promoted) {
// Promoted instance takes base priority + 1
priority = rsc->priority + 1;
} else {
priority = rsc->priority;
}
node->details->priority += priority;
pcmk__rsc_trace(rsc, "%s now has priority %d with %s'%s' (priority: %d%s)",
pcmk__node_name(node), node->details->priority,
(rsc->role == pcmk_role_promoted)? "promoted " : "",
rsc->id, rsc->priority,
(rsc->role == pcmk_role_promoted)? " + 1" : "");
/* Priority of a resource running on a guest node is added to the cluster
* node as well. */
if (node->details->remote_rsc
&& node->details->remote_rsc->container) {
GList *gIter = node->details->remote_rsc->container->running_on;
for (; gIter != NULL; gIter = gIter->next) {
pcmk_node_t *a_node = gIter->data;
a_node->details->priority += priority;
pcmk__rsc_trace(rsc,
"%s now has priority %d with %s'%s' "
"(priority: %d%s) from guest node %s",
pcmk__node_name(a_node), a_node->details->priority,
(rsc->role == pcmk_role_promoted)? "promoted " : "",
rsc->id, rsc->priority,
(rsc->role == pcmk_role_promoted)? " + 1" : "",
pcmk__node_name(node));
}
}
}
void
native_add_running(pcmk_resource_t *rsc, pcmk_node_t *node,
pcmk_scheduler_t *scheduler, gboolean failed)
{
GList *gIter = rsc->running_on;
CRM_CHECK(node != NULL, return);
for (; gIter != NULL; gIter = gIter->next) {
pcmk_node_t *a_node = (pcmk_node_t *) gIter->data;
CRM_CHECK(a_node != NULL, return);
if (pcmk__str_eq(a_node->details->id, node->details->id, pcmk__str_casei)) {
return;
}
}
pcmk__rsc_trace(rsc, "Adding %s to %s %s", rsc->id, pcmk__node_name(node),
pcmk_is_set(rsc->flags, pcmk_rsc_managed)? "" : "(unmanaged)");
rsc->running_on = g_list_append(rsc->running_on, node);
if (rsc->variant == pcmk_rsc_variant_primitive) {
node->details->running_rsc = g_list_append(node->details->running_rsc, rsc);
native_priority_to_node(rsc, node, failed);
}
if ((rsc->variant == pcmk_rsc_variant_primitive)
&& node->details->maintenance) {
pcmk__clear_rsc_flags(rsc, pcmk_rsc_managed);
pcmk__set_rsc_flags(rsc, pcmk_rsc_maintenance);
}
if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
pcmk_resource_t *p = rsc->parent;
pcmk__rsc_info(rsc, "resource %s isn't managed", rsc->id);
resource_location(rsc, node, INFINITY, "not_managed_default",
scheduler);
while(p && node->details->online) {
/* add without the additional location constraint */
p->running_on = g_list_append(p->running_on, node);
p = p->parent;
}
return;
}
if (is_multiply_active(rsc)) {
switch (rsc->recovery_type) {
case pcmk_multiply_active_stop:
{
GHashTableIter gIter;
pcmk_node_t *local_node = NULL;
/* make sure it doesn't come up again */
if (rsc->allowed_nodes != NULL) {
g_hash_table_destroy(rsc->allowed_nodes);
}
rsc->allowed_nodes = pe__node_list2table(scheduler->nodes);
g_hash_table_iter_init(&gIter, rsc->allowed_nodes);
while (g_hash_table_iter_next(&gIter, NULL, (void **)&local_node)) {
local_node->weight = -INFINITY;
}
}
break;
case pcmk_multiply_active_block:
pcmk__clear_rsc_flags(rsc, pcmk_rsc_managed);
pcmk__set_rsc_flags(rsc, pcmk_rsc_blocked);
/* If the resource belongs to a group or bundle configured with
* PCMK_META_MULTIPLE_ACTIVE=PCMK_VALUE_BLOCK, block the entire
* entity.
*/
if (rsc->parent
&& ((rsc->parent->variant == pcmk_rsc_variant_group)
|| (rsc->parent->variant == pcmk_rsc_variant_bundle))
&& (rsc->parent->recovery_type == pcmk_multiply_active_block)) {
GList *gIter = rsc->parent->children;
for (; gIter != NULL; gIter = gIter->next) {
pcmk_resource_t *child = gIter->data;
pcmk__clear_rsc_flags(child, pcmk_rsc_managed);
pcmk__set_rsc_flags(child, pcmk_rsc_blocked);
}
}
break;
// pcmk_multiply_active_restart, pcmk_multiply_active_unexpected
default:
/* The scheduler will do the right thing because the relevant
* variables and flags are set when unpacking the history.
*/
break;
}
crm_debug("%s is active on multiple nodes including %s: %s",
rsc->id, pcmk__node_name(node),
pcmk_multiply_active_text(rsc->recovery_type));
} else {
pcmk__rsc_trace(rsc, "Resource %s is active on %s",
rsc->id, pcmk__node_name(node));
}
if (rsc->parent != NULL) {
native_add_running(rsc->parent, node, scheduler, FALSE);
}
}
static void
recursive_clear_unique(pcmk_resource_t *rsc, gpointer user_data)
{
pcmk__clear_rsc_flags(rsc, pcmk_rsc_unique);
add_hash_param(rsc->meta, PCMK_META_GLOBALLY_UNIQUE, PCMK_VALUE_FALSE);
g_list_foreach(rsc->children, (GFunc) recursive_clear_unique, NULL);
}
gboolean
native_unpack(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler)
{
pcmk_resource_t *parent = uber_parent(rsc);
const char *standard = crm_element_value(rsc->xml, PCMK_XA_CLASS);
uint32_t ra_caps = pcmk_get_ra_caps(standard);
pcmk__rsc_trace(rsc, "Processing resource %s...", rsc->id);
// Only some agent standards support unique and promotable clones
if (!pcmk_is_set(ra_caps, pcmk_ra_cap_unique)
&& pcmk_is_set(rsc->flags, pcmk_rsc_unique)
&& pe_rsc_is_clone(parent)) {
/* @COMPAT We should probably reject this situation as an error (as we
* do for promotable below) rather than warn and convert, but that would
* be a backward-incompatible change that we should probably do with a
* transform at a schema major version bump.
*/
pe__force_anon(standard, parent, rsc->id, scheduler);
/* Clear PCMK_META_GLOBALLY_UNIQUE on the parent and all its descendants
* unpacked so far (clearing the parent should make any future children
* unpacking correct). We have to clear this resource explicitly because
* it isn't hooked into the parent's children yet.
*/
recursive_clear_unique(parent, NULL);
recursive_clear_unique(rsc, NULL);
}
if (!pcmk_is_set(ra_caps, pcmk_ra_cap_promotable)
&& pcmk_is_set(parent->flags, pcmk_rsc_promotable)) {
pcmk__config_err("Resource %s is of type %s and therefore "
"cannot be used as a promotable clone resource",
rsc->id, standard);
return FALSE;
}
return TRUE;
}
static bool
rsc_is_on_node(pcmk_resource_t *rsc, const pcmk_node_t *node, int flags)
{
pcmk__rsc_trace(rsc, "Checking whether %s is on %s",
rsc->id, pcmk__node_name(node));
if (pcmk_is_set(flags, pcmk_rsc_match_current_node)
&& (rsc->running_on != NULL)) {
for (GList *iter = rsc->running_on; iter; iter = iter->next) {
if (pcmk__same_node((pcmk_node_t *) iter->data, node)) {
return true;
}
}
} else if (pcmk_is_set(flags, pe_find_inactive) // @COMPAT deprecated
&& (rsc->running_on == NULL)) {
return true;
} else if (!pcmk_is_set(flags, pcmk_rsc_match_current_node)
&& (rsc->allocated_to != NULL)
&& pcmk__same_node(rsc->allocated_to, node)) {
return true;
}
return false;
}
pcmk_resource_t *
native_find_rsc(pcmk_resource_t *rsc, const char *id,
const pcmk_node_t *on_node, int flags)
{
bool match = false;
pcmk_resource_t *result = NULL;
CRM_CHECK(id && rsc && rsc->id, return NULL);
if (pcmk_is_set(flags, pcmk_rsc_match_clone_only)) {
const char *rid = ID(rsc->xml);
if (!pe_rsc_is_clone(pe__const_top_resource(rsc, false))) {
match = false;
} else if (!strcmp(id, rsc->id) || pcmk__str_eq(id, rid, pcmk__str_none)) {
match = true;
}
} else if (!strcmp(id, rsc->id)) {
match = true;
} else if (pcmk_is_set(flags, pcmk_rsc_match_history)
&& rsc->clone_name && strcmp(rsc->clone_name, id) == 0) {
match = true;
} else if (pcmk_is_set(flags, pcmk_rsc_match_basename)
|| (pcmk_is_set(flags, pcmk_rsc_match_anon_basename)
&& !pcmk_is_set(rsc->flags, pcmk_rsc_unique))) {
match = pe_base_name_eq(rsc, id);
}
if (match && on_node) {
if (!rsc_is_on_node(rsc, on_node, flags)) {
match = false;
}
}
if (match) {
return rsc;
}
for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
pcmk_resource_t *child = (pcmk_resource_t *) gIter->data;
result = rsc->fns->find_rsc(child, id, on_node, flags);
if (result) {
return result;
}
}
return NULL;
}
// create is ignored
char *
native_parameter(pcmk_resource_t *rsc, pcmk_node_t *node, gboolean create,
const char *name, pcmk_scheduler_t *scheduler)
{
char *value_copy = NULL;
const char *value = NULL;
GHashTable *params = NULL;
CRM_CHECK(rsc != NULL, return NULL);
CRM_CHECK(name != NULL && strlen(name) != 0, return NULL);
pcmk__rsc_trace(rsc, "Looking up %s in %s", name, rsc->id);
params = pe_rsc_params(rsc, node, scheduler);
value = g_hash_table_lookup(params, name);
if (value == NULL) {
/* try meta attributes instead */
value = g_hash_table_lookup(rsc->meta, name);
}
pcmk__str_update(&value_copy, value);
return value_copy;
}
gboolean
native_active(pcmk_resource_t * rsc, gboolean all)
{
for (GList *gIter = rsc->running_on; gIter != NULL; gIter = gIter->next) {
pcmk_node_t *a_node = (pcmk_node_t *) gIter->data;
if (a_node->details->unclean) {
pcmk__rsc_trace(rsc, "Resource %s: %s is unclean",
rsc->id, pcmk__node_name(a_node));
return TRUE;
} else if (!a_node->details->online
&& pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
pcmk__rsc_trace(rsc, "Resource %s: %s is offline",
rsc->id, pcmk__node_name(a_node));
} else {
pcmk__rsc_trace(rsc, "Resource %s active on %s",
rsc->id, pcmk__node_name(a_node));
return TRUE;
}
}
return FALSE;
}
struct print_data_s {
long options;
void *print_data;
};
static const char *
native_pending_state(const pcmk_resource_t *rsc)
{
const char *pending_state = NULL;
if (pcmk__str_eq(rsc->pending_task, PCMK_ACTION_START, pcmk__str_casei)) {
pending_state = "Starting";
} else if (pcmk__str_eq(rsc->pending_task, PCMK_ACTION_STOP,
pcmk__str_casei)) {
pending_state = "Stopping";
} else if (pcmk__str_eq(rsc->pending_task, PCMK_ACTION_MIGRATE_TO,
pcmk__str_casei)) {
pending_state = "Migrating";
} else if (pcmk__str_eq(rsc->pending_task, PCMK_ACTION_MIGRATE_FROM,
pcmk__str_casei)) {
/* Work might be done in here. */
pending_state = "Migrating";
} else if (pcmk__str_eq(rsc->pending_task, PCMK_ACTION_PROMOTE,
pcmk__str_casei)) {
pending_state = "Promoting";
} else if (pcmk__str_eq(rsc->pending_task, PCMK_ACTION_DEMOTE,
pcmk__str_casei)) {
pending_state = "Demoting";
}
return pending_state;
}
static const char *
native_pending_task(const pcmk_resource_t *rsc)
{
const char *pending_task = NULL;
if (pcmk__str_eq(rsc->pending_task, PCMK_ACTION_MONITOR, pcmk__str_casei)) {
pending_task = "Monitoring";
/* Pending probes are not printed, even if pending
* operations are requested. If someone ever requests that
* behavior, uncomment this and the corresponding part of
* unpack.c:unpack_rsc_op().
*/
/*
} else if (pcmk__str_eq(rsc->pending_task, "probe", pcmk__str_casei)) {
pending_task = "Checking";
*/
}
return pending_task;
}
static enum rsc_role_e
native_displayable_role(const pcmk_resource_t *rsc)
{
enum rsc_role_e role = rsc->role;
if ((role == pcmk_role_started)
&& pcmk_is_set(pe__const_top_resource(rsc, false)->flags,
pcmk_rsc_promotable)) {
role = pcmk_role_unpromoted;
}
return role;
}
static const char *
native_displayable_state(const pcmk_resource_t *rsc, bool print_pending)
{
const char *rsc_state = NULL;
if (print_pending) {
rsc_state = native_pending_state(rsc);
}
if (rsc_state == NULL) {
rsc_state = pcmk_role_text(native_displayable_role(rsc));
}
return rsc_state;
}
/*!
* \internal
* \deprecated This function will be removed in a future release
*/
static void
native_print_xml(pcmk_resource_t *rsc, const char *pre_text, long options,
void *print_data)
{
const char *class = crm_element_value(rsc->xml, PCMK_XA_CLASS);
const char *prov = crm_element_value(rsc->xml, PCMK_XA_PROVIDER);
const char *rsc_state = native_displayable_state(rsc, pcmk_is_set(options, pe_print_pending));
const char *target_role = NULL;
/* resource information. */
status_print("%s<resource ", pre_text);
status_print(PCMK_XA_ID "=\"%s\" ", rsc_printable_id(rsc));
status_print("resource_agent=\"%s%s%s:%s\" ", class,
((prov == NULL)? "" : PROVIDER_SEP),
((prov == NULL)? "" : prov),
crm_element_value(rsc->xml, PCMK_XA_TYPE));
status_print("role=\"%s\" ", rsc_state);
if (rsc->meta) {
target_role = g_hash_table_lookup(rsc->meta, PCMK_META_TARGET_ROLE);
}
if (target_role) {
status_print("target_role=\"%s\" ", target_role);
}
status_print("active=\"%s\" ", pcmk__btoa(rsc->fns->active(rsc, TRUE)));
status_print("orphaned=\"%s\" ",
pcmk__flag_text(rsc->flags, pcmk_rsc_removed));
status_print("blocked=\"%s\" ",
pcmk__flag_text(rsc->flags, pcmk_rsc_blocked));
status_print("managed=\"%s\" ",
pcmk__flag_text(rsc->flags, pcmk_rsc_managed));
status_print("failed=\"%s\" ",
pcmk__flag_text(rsc->flags, pcmk_rsc_failed));
status_print("failure_ignored=\"%s\" ",
pcmk__flag_text(rsc->flags, pcmk_rsc_ignore_failure));
status_print("nodes_running_on=\"%d\" ", g_list_length(rsc->running_on));
if (options & pe_print_pending) {
const char *pending_task = native_pending_task(rsc);
if (pending_task) {
status_print("pending=\"%s\" ", pending_task);
}
}
/* print out the nodes this resource is running on */
if (options & pe_print_rsconly) {
status_print("/>\n");
/* do nothing */
} else if (rsc->running_on != NULL) {
GList *gIter = rsc->running_on;
status_print(">\n");
for (; gIter != NULL; gIter = gIter->next) {
pcmk_node_t *node = (pcmk_node_t *) gIter->data;
status_print("%s <node name=\"%s\" " PCMK_XA_ID "=\"%s\" "
"cached=\"%s\"/>\n",
pre_text, pcmk__s(node->details->uname, ""),
node->details->id, pcmk__btoa(!node->details->online));
}
status_print("%s</resource>\n", pre_text);
} else {
status_print("/>\n");
}
}
// Append a flag to resource description string's flags list
static bool
add_output_flag(GString *s, const char *flag_desc, bool have_flags)
{
g_string_append(s, (have_flags? ", " : " ("));
g_string_append(s, flag_desc);
return true;
}
// Append a node name to resource description string's node list
static bool
add_output_node(GString *s, const char *node, bool have_nodes)
{
g_string_append(s, (have_nodes? " " : " [ "));
g_string_append(s, node);
return true;
}
/*!
* \internal
* \brief Create a string description of a resource
*
* \param[in] rsc Resource to describe
* \param[in] name Desired identifier for the resource
* \param[in] node If not NULL, node that resource is "on"
* \param[in] show_opts Bitmask of pcmk_show_opt_e.
* \param[in] target_role Resource's target role
* \param[in] show_nodes Whether to display nodes when multiply active
*
* \return Newly allocated string description of resource
* \note Caller must free the result with g_free().
*/
gchar *
pcmk__native_output_string(const pcmk_resource_t *rsc, const char *name,
const pcmk_node_t *node, uint32_t show_opts,
const char *target_role, bool show_nodes)
{
const char *class = crm_element_value(rsc->xml, PCMK_XA_CLASS);
const char *provider = NULL;
const char *kind = crm_element_value(rsc->xml, PCMK_XA_TYPE);
GString *outstr = NULL;
bool have_flags = false;
if (rsc->variant != pcmk_rsc_variant_primitive) {
return NULL;
}
CRM_CHECK(name != NULL, name = "unknown");
CRM_CHECK(kind != NULL, kind = "unknown");
CRM_CHECK(class != NULL, class = "unknown");
if (pcmk_is_set(pcmk_get_ra_caps(class), pcmk_ra_cap_provider)) {
provider = crm_element_value(rsc->xml, PCMK_XA_PROVIDER);
}
if ((node == NULL) && (rsc->lock_node != NULL)) {
node = rsc->lock_node;
}
if (pcmk_any_flags_set(show_opts, pcmk_show_rsc_only)
|| pcmk__list_of_multiple(rsc->running_on)) {
node = NULL;
}
outstr = g_string_sized_new(128);
// Resource name and agent
pcmk__g_strcat(outstr,
name, "\t(", class, ((provider == NULL)? "" : PROVIDER_SEP),
pcmk__s(provider, ""), ":", kind, "):\t", NULL);
// State on node
if (pcmk_is_set(rsc->flags, pcmk_rsc_removed)) {
g_string_append(outstr, " ORPHANED");
}
if (pcmk_is_set(rsc->flags, pcmk_rsc_failed)) {
enum rsc_role_e role = native_displayable_role(rsc);
g_string_append(outstr, " FAILED");
if (role > pcmk_role_unpromoted) {
pcmk__add_word(&outstr, 0, pcmk_role_text(role));
}
} else {
bool show_pending = pcmk_is_set(show_opts, pcmk_show_pending);
pcmk__add_word(&outstr, 0, native_displayable_state(rsc, show_pending));
}
if (node) {
pcmk__add_word(&outstr, 0, pcmk__node_name(node));
}
// Failed probe operation
if (native_displayable_role(rsc) == pcmk_role_stopped) {
xmlNode *probe_op = pe__failed_probe_for_rsc(rsc, node ? node->details->uname : NULL);
if (probe_op != NULL) {
int rc;
pcmk__scan_min_int(crm_element_value(probe_op, PCMK__XA_RC_CODE),
&rc, 0);
pcmk__g_strcat(outstr, " (", services_ocf_exitcode_str(rc), ") ",
NULL);
}
}
// Flags, as: (<flag> [...])
if (node && !(node->details->online) && node->details->unclean) {
have_flags = add_output_flag(outstr, "UNCLEAN", have_flags);
}
if (node && (node == rsc->lock_node)) {
have_flags = add_output_flag(outstr, "LOCKED", have_flags);
}
if (pcmk_is_set(show_opts, pcmk_show_pending)) {
const char *pending_task = native_pending_task(rsc);
if (pending_task) {
have_flags = add_output_flag(outstr, pending_task, have_flags);
}
}
if (target_role != NULL) {
switch (pcmk_parse_role(target_role)) {
case pcmk_role_unknown:
pcmk__config_err("Invalid " PCMK_META_TARGET_ROLE
" %s for resource %s", target_role, rsc->id);
break;
case pcmk_role_stopped:
have_flags = add_output_flag(outstr, "disabled", have_flags);
break;
case pcmk_role_unpromoted:
if (pcmk_is_set(pe__const_top_resource(rsc, false)->flags,
pcmk_rsc_promotable)) {
have_flags = add_output_flag(outstr,
PCMK_META_TARGET_ROLE ":",
have_flags);
g_string_append(outstr, target_role);
}
break;
default:
/* Only show target role if it limits our abilities (i.e. ignore
* Started, as it is the default anyways, and doesn't prevent
* the resource from becoming promoted).
*/
break;
}
}
// Blocked or maintenance implies unmanaged
if (pcmk_any_flags_set(rsc->flags,
pcmk_rsc_blocked|pcmk_rsc_maintenance)) {
if (pcmk_is_set(rsc->flags, pcmk_rsc_blocked)) {
have_flags = add_output_flag(outstr, "blocked", have_flags);
} else if (pcmk_is_set(rsc->flags, pcmk_rsc_maintenance)) {
have_flags = add_output_flag(outstr, "maintenance", have_flags);
}
} else if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
have_flags = add_output_flag(outstr, "unmanaged", have_flags);
}
if (pcmk_is_set(rsc->flags, pcmk_rsc_ignore_failure)) {
have_flags = add_output_flag(outstr, "failure ignored", have_flags);
}
if (have_flags) {
g_string_append_c(outstr, ')');
}
// User-supplied description
if (pcmk_any_flags_set(show_opts, pcmk_show_rsc_only|pcmk_show_description)
|| pcmk__list_of_multiple(rsc->running_on)) {
const char *desc = crm_element_value(rsc->xml, PCMK_XA_DESCRIPTION);
if (desc) {
g_string_append(outstr, " (");
g_string_append(outstr, desc);
g_string_append(outstr, ")");
}
}
if (show_nodes && !pcmk_is_set(show_opts, pcmk_show_rsc_only)
&& pcmk__list_of_multiple(rsc->running_on)) {
bool have_nodes = false;
for (GList *iter = rsc->running_on; iter != NULL; iter = iter->next) {
pcmk_node_t *n = (pcmk_node_t *) iter->data;
have_nodes = add_output_node(outstr, n->details->uname, have_nodes);
}
if (have_nodes) {
g_string_append(outstr, " ]");
}
}
return g_string_free(outstr, FALSE);
}
int
pe__common_output_html(pcmk__output_t *out, const pcmk_resource_t *rsc,
const char *name, const pcmk_node_t *node,
uint32_t show_opts)
{
const char *kind = crm_element_value(rsc->xml, PCMK_XA_TYPE);
const char *target_role = NULL;
xmlNodePtr list_node = NULL;
const char *cl = NULL;
CRM_ASSERT(rsc->variant == pcmk_rsc_variant_primitive);
CRM_ASSERT(kind != NULL);
if (rsc->meta) {
const char *is_internal = g_hash_table_lookup(rsc->meta,
PCMK__META_INTERNAL_RSC);
if (crm_is_true(is_internal)
&& !pcmk_is_set(show_opts, pcmk_show_implicit_rscs)) {
crm_trace("skipping print of internal resource %s", rsc->id);
return pcmk_rc_no_output;
}
target_role = g_hash_table_lookup(rsc->meta, PCMK_META_TARGET_ROLE);
}
if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
cl = "rsc-managed";
} else if (pcmk_is_set(rsc->flags, pcmk_rsc_failed)) {
cl = "rsc-failed";
} else if ((rsc->variant == pcmk_rsc_variant_primitive)
&& (rsc->running_on == NULL)) {
cl = "rsc-failed";
} else if (pcmk__list_of_multiple(rsc->running_on)) {
cl = "rsc-multiple";
} else if (pcmk_is_set(rsc->flags, pcmk_rsc_ignore_failure)) {
cl = "rsc-failure-ignored";
} else {
cl = "rsc-ok";
}
{
gchar *s = pcmk__native_output_string(rsc, name, node, show_opts,
target_role, true);
list_node = pcmk__output_create_html_node(out, "li", NULL, NULL, NULL);
pcmk_create_html_node(list_node, "span", NULL, cl, s);
g_free(s);
}
return pcmk_rc_ok;
}
int
pe__common_output_text(pcmk__output_t *out, const pcmk_resource_t *rsc,
const char *name, const pcmk_node_t *node,
uint32_t show_opts)
{
const char *target_role = NULL;
CRM_ASSERT(rsc->variant == pcmk_rsc_variant_primitive);
if (rsc->meta) {
const char *is_internal = g_hash_table_lookup(rsc->meta,
PCMK__META_INTERNAL_RSC);
if (crm_is_true(is_internal)
&& !pcmk_is_set(show_opts, pcmk_show_implicit_rscs)) {
crm_trace("skipping print of internal resource %s", rsc->id);
return pcmk_rc_no_output;
}
target_role = g_hash_table_lookup(rsc->meta, PCMK_META_TARGET_ROLE);
}
{
gchar *s = pcmk__native_output_string(rsc, name, node, show_opts,
target_role, true);
out->list_item(out, NULL, "%s", s);
g_free(s);
}
return pcmk_rc_ok;
}
/*!
* \internal
* \deprecated This function will be removed in a future release
*/
void
common_print(pcmk_resource_t *rsc, const char *pre_text, const char *name,
const pcmk_node_t *node, long options, void *print_data)
{
const char *target_role = NULL;
CRM_ASSERT(rsc->variant == pcmk_rsc_variant_primitive);
if (rsc->meta) {
const char *is_internal = g_hash_table_lookup(rsc->meta,
PCMK__META_INTERNAL_RSC);
if (crm_is_true(is_internal)
&& !pcmk_is_set(options, pe_print_implicit)) {
crm_trace("skipping print of internal resource %s", rsc->id);
return;
}
target_role = g_hash_table_lookup(rsc->meta, PCMK_META_TARGET_ROLE);
}
if (options & pe_print_xml) {
native_print_xml(rsc, pre_text, options, print_data);
return;
}
if ((pre_text == NULL) && (options & pe_print_printf)) {
pre_text = " ";
}
if (options & pe_print_html) {
if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
status_print("<font color=\"yellow\">");
} else if (pcmk_is_set(rsc->flags, pcmk_rsc_failed)) {
status_print("<font color=\"red\">");
} else if (rsc->running_on == NULL) {
status_print("<font color=\"red\">");
} else if (pcmk__list_of_multiple(rsc->running_on)) {
status_print("<font color=\"orange\">");
} else if (pcmk_is_set(rsc->flags, pcmk_rsc_ignore_failure)) {
status_print("<font color=\"yellow\">");
} else {
status_print("<font color=\"green\">");
}
}
{
gchar *resource_s = pcmk__native_output_string(rsc, name, node, options,
target_role, false);
status_print("%s%s", (pre_text? pre_text : ""), resource_s);
g_free(resource_s);
}
if (pcmk_is_set(options, pe_print_html)) {
status_print(" </font> ");
}
if (!pcmk_is_set(options, pe_print_rsconly)
&& pcmk__list_of_multiple(rsc->running_on)) {
GList *gIter = rsc->running_on;
int counter = 0;
if (options & pe_print_html) {
status_print("<ul>\n");
} else if ((options & pe_print_printf)
|| (options & pe_print_ncurses)) {
status_print("[");
}
for (; gIter != NULL; gIter = gIter->next) {
pcmk_node_t *n = (pcmk_node_t *) gIter->data;
counter++;
if (options & pe_print_html) {
status_print("<li>\n%s", pcmk__node_name(n));
} else if ((options & pe_print_printf)
|| (options & pe_print_ncurses)) {
status_print(" %s", pcmk__node_name(n));
} else if ((options & pe_print_log)) {
status_print("\t%d : %s", counter, pcmk__node_name(n));
} else {
status_print("%s", pcmk__node_name(n));
}
if (options & pe_print_html) {
status_print("</li>\n");
}
}
if (options & pe_print_html) {
status_print("</ul>\n");
} else if ((options & pe_print_printf)
|| (options & pe_print_ncurses)) {
status_print(" ]");
}
}
if (options & pe_print_html) {
status_print("<br/>\n");
} else if (options & pe_print_suppres_nl) {
/* nothing */
} else if ((options & pe_print_printf) || (options & pe_print_ncurses)) {
status_print("\n");
}
}
/*!
* \internal
* \deprecated This function will be removed in a future release
*/
void
native_print(pcmk_resource_t *rsc, const char *pre_text, long options,
void *print_data)
{
const pcmk_node_t *node = NULL;
CRM_ASSERT(rsc->variant == pcmk_rsc_variant_primitive);
if (options & pe_print_xml) {
native_print_xml(rsc, pre_text, options, print_data);
return;
}
node = pcmk__current_node(rsc);
if (node == NULL) {
// This is set only if a non-probe action is pending on this node
node = rsc->pending_node;
}
common_print(rsc, pre_text, rsc_printable_id(rsc), node, options, print_data);
}
PCMK__OUTPUT_ARGS("primitive", "uint32_t", "pcmk_resource_t *", "GList *",
"GList *")
int
pe__resource_xml(pcmk__output_t *out, va_list args)
{
uint32_t show_opts = va_arg(args, uint32_t);
pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
GList *only_node G_GNUC_UNUSED = va_arg(args, GList *);
GList *only_rsc = va_arg(args, GList *);
bool print_pending = pcmk_is_set(show_opts, pcmk_show_pending);
const char *class = crm_element_value(rsc->xml, PCMK_XA_CLASS);
const char *prov = crm_element_value(rsc->xml, PCMK_XA_PROVIDER);
const char *rsc_state = native_displayable_state(rsc, print_pending);
const char *desc = NULL;
char ra_name[LINE_MAX];
char *nodes_running_on = NULL;
const char *lock_node_name = NULL;
int rc = pcmk_rc_no_output;
const char *target_role = NULL;
const char *maintenance = pcmk__flag_text(rsc->flags, pcmk_rsc_maintenance);
const char *managed = pcmk__flag_text(rsc->flags, pcmk_rsc_managed);
const char *failed = pcmk__flag_text(rsc->flags, pcmk_rsc_failed);
const char *ignored = pcmk__flag_text(rsc->flags, pcmk_rsc_ignore_failure);
desc = pe__resource_description(rsc, show_opts);
if (rsc->meta != NULL) {
- target_role = g_hash_table_lookup(rsc->meta, PCMK_META_TARGET_ROLE);
+ target_role = g_hash_table_lookup(rsc->meta, PCMK_META_TARGET_ROLE);
}
CRM_ASSERT(rsc->variant == pcmk_rsc_variant_primitive);
if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
return pcmk_rc_no_output;
}
/* resource information. */
snprintf(ra_name, LINE_MAX, "%s%s%s:%s", class,
((prov == NULL)? "" : PROVIDER_SEP), ((prov == NULL)? "" : prov),
crm_element_value(rsc->xml, PCMK_XA_TYPE));
nodes_running_on = pcmk__itoa(g_list_length(rsc->running_on));
if (rsc->lock_node != NULL) {
lock_node_name = rsc->lock_node->details->uname;
}
rc = pe__name_and_nvpairs_xml(out, true, PCMK_XE_RESOURCE, 15,
PCMK_XA_ID, rsc_printable_id(rsc),
"resource_agent", ra_name,
PCMK_XA_ROLE, rsc_state,
- "target_role", target_role,
+ PCMK_XA_TARGET_ROLE, target_role,
"active", pcmk__btoa(rsc->fns->active(rsc, TRUE)),
"orphaned", pcmk__flag_text(rsc->flags, pcmk_rsc_removed),
"blocked", pcmk__flag_text(rsc->flags, pcmk_rsc_blocked),
PCMK_XA_MAINTENANCE, maintenance,
PCMK_XA_MANAGED, managed,
PCMK_XA_FAILED, failed,
PCMK_XA_FAILURE_IGNORED, ignored,
"nodes_running_on", nodes_running_on,
PCMK_XA_PENDING, (print_pending? native_pending_task(rsc) : NULL),
"locked_to", lock_node_name,
PCMK_XA_DESCRIPTION, desc);
free(nodes_running_on);
CRM_ASSERT(rc == pcmk_rc_ok);
if (rsc->running_on != NULL) {
GList *gIter = rsc->running_on;
for (; gIter != NULL; gIter = gIter->next) {
pcmk_node_t *node = (pcmk_node_t *) gIter->data;
rc = pe__name_and_nvpairs_xml(out, false, PCMK_XE_NODE, 3,
PCMK_XA_NAME, node->details->uname,
PCMK_XA_ID, node->details->id,
"cached", pcmk__btoa(node->details->online));
CRM_ASSERT(rc == pcmk_rc_ok);
}
}
pcmk__output_xml_pop_parent(out);
return rc;
}
PCMK__OUTPUT_ARGS("primitive", "uint32_t", "pcmk_resource_t *", "GList *",
"GList *")
int
pe__resource_html(pcmk__output_t *out, va_list args)
{
uint32_t show_opts = va_arg(args, uint32_t);
pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
GList *only_node G_GNUC_UNUSED = va_arg(args, GList *);
GList *only_rsc = va_arg(args, GList *);
const pcmk_node_t *node = pcmk__current_node(rsc);
if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
return pcmk_rc_no_output;
}
CRM_ASSERT(rsc->variant == pcmk_rsc_variant_primitive);
if (node == NULL) {
// This is set only if a non-probe action is pending on this node
node = rsc->pending_node;
}
return pe__common_output_html(out, rsc, rsc_printable_id(rsc), node, show_opts);
}
PCMK__OUTPUT_ARGS("primitive", "uint32_t", "pcmk_resource_t *", "GList *",
"GList *")
int
pe__resource_text(pcmk__output_t *out, va_list args)
{
uint32_t show_opts = va_arg(args, uint32_t);
pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
GList *only_node G_GNUC_UNUSED = va_arg(args, GList *);
GList *only_rsc = va_arg(args, GList *);
const pcmk_node_t *node = pcmk__current_node(rsc);
CRM_ASSERT(rsc->variant == pcmk_rsc_variant_primitive);
if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
return pcmk_rc_no_output;
}
if (node == NULL) {
// This is set only if a non-probe action is pending on this node
node = rsc->pending_node;
}
return pe__common_output_text(out, rsc, rsc_printable_id(rsc), node, show_opts);
}
void
native_free(pcmk_resource_t * rsc)
{
pcmk__rsc_trace(rsc, "Freeing resource action list (not the data)");
common_free(rsc);
}
enum rsc_role_e
native_resource_state(const pcmk_resource_t * rsc, gboolean current)
{
enum rsc_role_e role = rsc->next_role;
if (current) {
role = rsc->role;
}
pcmk__rsc_trace(rsc, "%s state: %s", rsc->id, pcmk_role_text(role));
return role;
}
/*!
* \internal
* \brief List nodes where a resource (or any of its children) is
*
* \param[in] rsc Resource to check
* \param[out] list List to add result to
* \param[in] current 0 = where allocated, 1 = where running,
* 2 = where running or pending
*
* \return If list contains only one node, that node, or NULL otherwise
*/
pcmk_node_t *
native_location(const pcmk_resource_t *rsc, GList **list, int current)
{
// @COMPAT: Accept a pcmk__rsc_node argument instead of int current
pcmk_node_t *one = NULL;
GList *result = NULL;
if (rsc->children) {
GList *gIter = rsc->children;
for (; gIter != NULL; gIter = gIter->next) {
pcmk_resource_t *child = (pcmk_resource_t *) gIter->data;
child->fns->location(child, &result, current);
}
} else if (current) {
if (rsc->running_on) {
result = g_list_copy(rsc->running_on);
}
if ((current == 2) && rsc->pending_node
&& !pe_find_node_id(result, rsc->pending_node->details->id)) {
result = g_list_append(result, rsc->pending_node);
}
} else if (current == FALSE && rsc->allocated_to) {
result = g_list_append(NULL, rsc->allocated_to);
}
if (result && (result->next == NULL)) {
one = result->data;
}
if (list) {
GList *gIter = result;
for (; gIter != NULL; gIter = gIter->next) {
pcmk_node_t *node = (pcmk_node_t *) gIter->data;
if (*list == NULL || pe_find_node_id(*list, node->details->id) == NULL) {
*list = g_list_append(*list, node);
}
}
}
g_list_free(result);
return one;
}
static void
get_rscs_brief(GList *rsc_list, GHashTable * rsc_table, GHashTable * active_table)
{
GList *gIter = rsc_list;
for (; gIter != NULL; gIter = gIter->next) {
pcmk_resource_t *rsc = (pcmk_resource_t *) gIter->data;
const char *class = crm_element_value(rsc->xml, PCMK_XA_CLASS);
const char *kind = crm_element_value(rsc->xml, PCMK_XA_TYPE);
int offset = 0;
char buffer[LINE_MAX];
int *rsc_counter = NULL;
int *active_counter = NULL;
if (rsc->variant != pcmk_rsc_variant_primitive) {
continue;
}
offset += snprintf(buffer + offset, LINE_MAX - offset, "%s", class);
if (pcmk_is_set(pcmk_get_ra_caps(class), pcmk_ra_cap_provider)) {
const char *prov = crm_element_value(rsc->xml, PCMK_XA_PROVIDER);
if (prov != NULL) {
offset += snprintf(buffer + offset, LINE_MAX - offset,
PROVIDER_SEP "%s", prov);
}
}
offset += snprintf(buffer + offset, LINE_MAX - offset, ":%s", kind);
CRM_LOG_ASSERT(offset > 0);
if (rsc_table) {
rsc_counter = g_hash_table_lookup(rsc_table, buffer);
if (rsc_counter == NULL) {
rsc_counter = calloc(1, sizeof(int));
*rsc_counter = 0;
g_hash_table_insert(rsc_table, strdup(buffer), rsc_counter);
}
(*rsc_counter)++;
}
if (active_table) {
GList *gIter2 = rsc->running_on;
for (; gIter2 != NULL; gIter2 = gIter2->next) {
pcmk_node_t *node = (pcmk_node_t *) gIter2->data;
GHashTable *node_table = NULL;
if (node->details->unclean == FALSE && node->details->online == FALSE &&
pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
continue;
}
node_table = g_hash_table_lookup(active_table, node->details->uname);
if (node_table == NULL) {
node_table = pcmk__strkey_table(free, free);
g_hash_table_insert(active_table, strdup(node->details->uname), node_table);
}
active_counter = g_hash_table_lookup(node_table, buffer);
if (active_counter == NULL) {
active_counter = calloc(1, sizeof(int));
*active_counter = 0;
g_hash_table_insert(node_table, strdup(buffer), active_counter);
}
(*active_counter)++;
}
}
}
}
static void
destroy_node_table(gpointer data)
{
GHashTable *node_table = data;
if (node_table) {
g_hash_table_destroy(node_table);
}
}
/*!
* \internal
* \deprecated This function will be removed in a future release
*/
void
print_rscs_brief(GList *rsc_list, const char *pre_text, long options,
void *print_data, gboolean print_all)
{
GHashTable *rsc_table = pcmk__strkey_table(free, free);
GHashTable *active_table = pcmk__strkey_table(free, destroy_node_table);
GHashTableIter hash_iter;
char *type = NULL;
int *rsc_counter = NULL;
get_rscs_brief(rsc_list, rsc_table, active_table);
g_hash_table_iter_init(&hash_iter, rsc_table);
while (g_hash_table_iter_next(&hash_iter, (gpointer *)&type, (gpointer *)&rsc_counter)) {
GHashTableIter hash_iter2;
char *node_name = NULL;
GHashTable *node_table = NULL;
int active_counter_all = 0;
g_hash_table_iter_init(&hash_iter2, active_table);
while (g_hash_table_iter_next(&hash_iter2, (gpointer *)&node_name, (gpointer *)&node_table)) {
int *active_counter = g_hash_table_lookup(node_table, type);
if (active_counter == NULL || *active_counter == 0) {
continue;
} else {
active_counter_all += *active_counter;
}
if (options & pe_print_rsconly) {
node_name = NULL;
}
if (options & pe_print_html) {
status_print("<li>\n");
}
if (print_all) {
status_print("%s%d/%d\t(%s):\tActive %s\n", pre_text ? pre_text : "",
active_counter ? *active_counter : 0,
rsc_counter ? *rsc_counter : 0, type,
active_counter && (*active_counter > 0) && node_name ? node_name : "");
} else {
status_print("%s%d\t(%s):\tActive %s\n", pre_text ? pre_text : "",
active_counter ? *active_counter : 0, type,
active_counter && (*active_counter > 0) && node_name ? node_name : "");
}
if (options & pe_print_html) {
status_print("</li>\n");
}
}
if (print_all && active_counter_all == 0) {
if (options & pe_print_html) {
status_print("<li>\n");
}
status_print("%s%d/%d\t(%s):\tActive\n", pre_text ? pre_text : "",
active_counter_all,
rsc_counter ? *rsc_counter : 0, type);
if (options & pe_print_html) {
status_print("</li>\n");
}
}
}
if (rsc_table) {
g_hash_table_destroy(rsc_table);
rsc_table = NULL;
}
if (active_table) {
g_hash_table_destroy(active_table);
active_table = NULL;
}
}
int
pe__rscs_brief_output(pcmk__output_t *out, GList *rsc_list, uint32_t show_opts)
{
GHashTable *rsc_table = pcmk__strkey_table(free, free);
GHashTable *active_table = pcmk__strkey_table(free, destroy_node_table);
GList *sorted_rscs;
int rc = pcmk_rc_no_output;
get_rscs_brief(rsc_list, rsc_table, active_table);
/* Make a list of the rsc_table keys so that it can be sorted. This is to make sure
* output order stays consistent between systems.
*/
sorted_rscs = g_hash_table_get_keys(rsc_table);
sorted_rscs = g_list_sort(sorted_rscs, (GCompareFunc) strcmp);
for (GList *gIter = sorted_rscs; gIter; gIter = gIter->next) {
char *type = (char *) gIter->data;
int *rsc_counter = g_hash_table_lookup(rsc_table, type);
GList *sorted_nodes = NULL;
int active_counter_all = 0;
/* Also make a list of the active_table keys so it can be sorted. If there's
* more than one instance of a type of resource running, we need the nodes to
* be sorted to make sure output order stays consistent between systems.
*/
sorted_nodes = g_hash_table_get_keys(active_table);
sorted_nodes = g_list_sort(sorted_nodes, (GCompareFunc) pcmk__numeric_strcasecmp);
for (GList *gIter2 = sorted_nodes; gIter2; gIter2 = gIter2->next) {
char *node_name = (char *) gIter2->data;
GHashTable *node_table = g_hash_table_lookup(active_table, node_name);
int *active_counter = NULL;
if (node_table == NULL) {
continue;
}
active_counter = g_hash_table_lookup(node_table, type);
if (active_counter == NULL || *active_counter == 0) {
continue;
} else {
active_counter_all += *active_counter;
}
if (pcmk_is_set(show_opts, pcmk_show_rsc_only)) {
node_name = NULL;
}
if (pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
out->list_item(out, NULL, "%d/%d\t(%s):\tActive %s",
*active_counter,
rsc_counter ? *rsc_counter : 0, type,
(*active_counter > 0) && node_name ? node_name : "");
} else {
out->list_item(out, NULL, "%d\t(%s):\tActive %s",
*active_counter, type,
(*active_counter > 0) && node_name ? node_name : "");
}
rc = pcmk_rc_ok;
}
if (pcmk_is_set(show_opts, pcmk_show_inactive_rscs) && active_counter_all == 0) {
out->list_item(out, NULL, "%d/%d\t(%s):\tActive",
active_counter_all,
rsc_counter ? *rsc_counter : 0, type);
rc = pcmk_rc_ok;
}
if (sorted_nodes) {
g_list_free(sorted_nodes);
}
}
if (rsc_table) {
g_hash_table_destroy(rsc_table);
rsc_table = NULL;
}
if (active_table) {
g_hash_table_destroy(active_table);
active_table = NULL;
}
if (sorted_rscs) {
g_list_free(sorted_rscs);
}
return rc;
}
gboolean
pe__native_is_filtered(const pcmk_resource_t *rsc, GList *only_rsc,
gboolean check_parent)
{
if (pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) ||
pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches)) {
return FALSE;
} else if (check_parent && rsc->parent) {
const pcmk_resource_t *up = pe__const_top_resource(rsc, true);
return up->fns->is_filtered(up, only_rsc, FALSE);
}
return TRUE;
}
/*!
* \internal
* \brief Get maximum primitive resource instances per node
*
* \param[in] rsc Primitive resource to check
*
* \return Maximum number of \p rsc instances that can be active on one node
*/
unsigned int
pe__primitive_max_per_node(const pcmk_resource_t *rsc)
{
CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_primitive));
return 1U;
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Jan 25, 12:03 PM (1 d, 19 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1322491
Default Alt Text
(116 KB)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment