Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F5519438
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
51 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/include/pacemaker.h b/include/pacemaker.h
index b2a73cd7c5..51bf585785 100644
--- a/include/pacemaker.h
+++ b/include/pacemaker.h
@@ -1,246 +1,264 @@
/*
- * Copyright 2019 the Pacemaker project contributors
+ * Copyright 2019-2021 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 PACEMAKER__H
# define PACEMAKER__H
#ifdef __cplusplus
extern "C" {
#endif
/**
* \file
* \brief High Level API
* \ingroup pacemaker
*/
-# include <crm/stonith-ng.h>
+# include <glib.h>
# include <libxml/tree.h>
+# include <crm/pengine/pe_types.h>
+
+# include <crm/stonith-ng.h>
/*!
* \brief Get controller status
*
* \param[in,out] xml The destination for the result, as an XML tree.
* \param[in] dest_node Destination node for request
* \param[in] message_timeout_ms Message timeout
*
* \return Standard Pacemaker return code
*/
int pcmk_controller_status(xmlNodePtr *xml, char *dest_node, unsigned int message_timeout_ms);
/*!
* \brief Get designated controller
*
* \param[in,out] xml The destination for the result, as an XML tree.
* \param[in] message_timeout_ms Message timeout
*
* \return Standard Pacemaker return code
*/
int pcmk_designated_controller(xmlNodePtr *xml, unsigned int message_timeout_ms);
/*!
* \brief Get pacemakerd status
*
* \param[in,out] xml The destination for the result, as an XML tree.
* \param[in] ipc_name IPC name for request
* \param[in] message_timeout_ms Message timeout
*
* \return Standard Pacemaker return code
*/
int pcmk_pacemakerd_status(xmlNodePtr *xml, char *ipc_name, unsigned int message_timeout_ms);
+/*!
+ * \brief Calculate and output resource operation digests
+ *
+ * \param[out] xml Where to store XML with result
+ * \param[in] rsc Resource to calculate digests for
+ * \param[in] node Node whose operation history should be used
+ * \param[in] overrides Hash table of configuration parameters to override
+ * \param[in] data_set Cluster working set (with status)
+ *
+ * \return Standard Pacemaker return code
+ */
+int pcmk_resource_digests(xmlNodePtr *xml, pe_resource_t *rsc,
+ pe_node_t *node, GHashTable *overrides,
+ pe_working_set_t *data_set);
+
#ifdef BUILD_PUBLIC_LIBPACEMAKER
/*!
* \brief Get nodes list
*
* \param[in,out] xml The destination for the result, as an XML tree.
*
* \return Standard Pacemaker return code
*/
int pcmk_list_nodes(xmlNodePtr *xml);
/*!
* \brief Perform a STONITH action.
*
* \param[in] st A connection to the STONITH API.
* \param[in] target The node receiving the action.
* \param[in] action The action to perform.
* \param[in] name Who requested the fence action?
* \param[in] timeout How long to wait for the operation to complete (in ms).
* \param[in] tolerance If a successful action for \p target happened within
* this many ms, return 0 without performing the action
* again.
* \param[in] delay Apply a fencing delay. Value -1 means disable also any
* static/random fencing delays from pcmk_delay_base/max.
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_action(stonith_t *st, const char *target, const char *action,
const char *name, unsigned int timeout, unsigned int tolerance,
int delay);
/*!
* \brief List the fencing operations that have occurred for a specific node.
*
* \note If \p xml is not NULL, it will be freed first and the previous
* contents lost.
*
* \param[in,out] xml The destination for the result, as an XML tree.
* \param[in] st A connection to the STONITH API.
* \param[in] target The node to get history for.
* \param[in] timeout How long to wait for the operation to complete (in ms).
* \param[in] quiet Suppress most output.
* \param[in] verbose Include additional output.
* \param[in] broadcast Gather fencing history from all nodes.
* \param[in] cleanup Clean up fencing history after listing.
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_history(xmlNodePtr *xml, stonith_t *st, char *target,
unsigned int timeout, bool quiet, int verbose,
bool broadcast, bool cleanup);
/*!
* \brief List all installed STONITH agents.
*
* \note If \p xml is not NULL, it will be freed first and the previous
* contents lost.
*
* \param[in,out] xml The destination for the result, as an XML tree.
* \param[in] st A connection to the STONITH API.
* \param[in] timeout How long to wait for the operation to complete (in ms).
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_installed(xmlNodePtr *xml, stonith_t *st, unsigned int timeout);
/*!
* \brief When was a device last fenced?
*
* \note If \p xml is not NULL, it will be freed first and the previous
* contents lost.
*
* \param[in,out] xml The destination for the result, as an XML tree.
* \param[in] target The node that was fenced.
* \param[in] as_nodeid
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_last(xmlNodePtr *xml, const char *target, bool as_nodeid);
/*!
* \brief List nodes that can be fenced.
*
* \note If \p xml is not NULL, it will be freed first and the previous
* contents lost.
*
* \param[in,out] xml The destination for the result, as an XML tree
* \param[in] st A connection to the STONITH API
* \param[in] device_id Resource ID of fence device to check
* \param[in] timeout How long to wait for the operation to complete (in ms)
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_list_targets(xmlNodePtr *xml, stonith_t *st,
const char *device_id, unsigned int timeout);
/*!
* \brief Get metadata for a resource.
*
* \note If \p xml is not NULL, it will be freed first and the previous
* contents lost.
*
* \param[in,out] xml The destination for the result, as an XML tree.
* \param[in] st A connection to the STONITH API.
* \param[in] agent The fence agent to get metadata for.
* \param[in] timeout How long to wait for the operation to complete (in ms).
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_metadata(xmlNodePtr *xml, stonith_t *st, char *agent,
unsigned int timeout);
/*!
* \brief List registered fence devices.
*
* \note If \p xml is not NULL, it will be freed first and the previous
* contents lost.
*
* \param[in,out] xml The destination for the result, as an XML tree.
* \param[in] st A connection to the STONITH API.
* \param[in] target If not NULL, only return devices that can fence
* this node.
* \param[in] timeout How long to wait for the operation to complete (in ms).
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_registered(xmlNodePtr *xml, stonith_t *st, char *target,
unsigned int timeout);
/*!
* \brief Register a fencing level for a specific node, node regex, or attribute.
*
* \p target can take three different forms:
* - name=value, in which case \p target is an attribute.
* - @pattern, in which case \p target is a node regex.
* - Otherwise, \p target is a node name.
*
* \param[in] st A connection to the STONITH API.
* \param[in] target The object to register a fencing level for.
* \param[in] fence_level Index number of level to add.
* \param[in] devices Devices to use in level.
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_register_level(stonith_t *st, char *target, int fence_level,
stonith_key_value_t *devices);
/*!
* \brief Unregister a fencing level for a specific node, node regex, or attribute.
*
* \p target can take three different forms:
* - name=value, in which case \p target is an attribute.
* - @pattern, in which case \p target is a node regex.
* - Otherwise, \p target is a node name.
*
* \param[in] st A connection to the STONITH API.
* \param[in] target The object to unregister a fencing level for.
* \param[in] fence_level Index number of level to remove.
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_unregister_level(stonith_t *st, char *target, int fence_level);
/*!
* \brief Validate a STONITH device configuration.
*
* \note If \p xml is not NULL, it will be freed first and the previous
* contents lost.
*
* \param[in,out] xml The destination for the result, as an XML tree.
* \param[in] st A connection to the STONITH API.
* \param[in] agent The agent to validate (for example, "fence_xvm").
* \param[in] id STONITH device ID (may be NULL).
* \param[in] params STONITH device configuration parameters.
* \param[in] timeout How long to wait for the operation to complete (in ms).
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_validate(xmlNodePtr *xml, stonith_t *st, const char *agent,
const char *id, stonith_key_value_t *params,
unsigned int timeout);
#endif
#ifdef __cplusplus
}
#endif
#endif
diff --git a/include/pcmki/pcmki_resource.h b/include/pcmki/pcmki_resource.h
index effa945d2a..9d2afb5d25 100644
--- a/include/pcmki/pcmki_resource.h
+++ b/include/pcmki/pcmki_resource.h
@@ -1,14 +1,21 @@
/*
* Copyright 2021 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 PCMKI_RESOURCE__H
#define PCMKI_RESOURCE__H
+#include <glib.h>
+
#include <crm/common/output_internal.h>
+#include <crm/pengine/pe_types.h>
+
+int pcmk__resource_digests(pcmk__output_t *out, pe_resource_t *rsc,
+ pe_node_t *node, GHashTable *overrides,
+ pe_working_set_t *data_set);
#endif /* PCMK_RESOURCE__H */
diff --git a/lib/pacemaker/pcmk_output.c b/lib/pacemaker/pcmk_output.c
index 500afd11ee..bc4b91a28a 100644
--- a/lib/pacemaker/pcmk_output.c
+++ b/lib/pacemaker/pcmk_output.c
@@ -1,567 +1,672 @@
/*
- * Copyright 2019-2020 the Pacemaker project contributors
+ * Copyright 2019-2021 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU General Public License version 2
* or later (GPLv2+) WITHOUT ANY WARRANTY.
*/
#include <crm_internal.h>
#include <crm/common/results.h>
#include <crm/common/output_internal.h>
#include <crm/stonith-ng.h>
#include <crm/fencing/internal.h>
+#include <crm/pengine/internal.h>
#include <libxml/tree.h>
#include <pacemaker-internal.h>
pcmk__supported_format_t pcmk__out_formats[] = {
PCMK__SUPPORTED_FORMAT_XML,
{ NULL, NULL, NULL }
};
int
pcmk__out_prologue(pcmk__output_t **out, xmlNodePtr *xml) {
int rc = pcmk_rc_ok;
if (*xml != NULL) {
xmlFreeNode(*xml);
}
pcmk__register_formats(NULL, pcmk__out_formats);
rc = pcmk__output_new(out, "xml", NULL, NULL);
if (rc != pcmk_rc_ok) {
return rc;
}
return rc;
}
void
pcmk__out_epilogue(pcmk__output_t *out, xmlNodePtr *xml, int retval) {
if (retval == pcmk_rc_ok) {
out->finish(out, 0, FALSE, (void **) xml);
}
pcmk__output_free(out);
}
static char *
colocations_header(pe_resource_t *rsc, pcmk__colocation_t *cons,
gboolean dependents) {
char *score = NULL;
char *retval = NULL;
score = score2char(cons->score);
if (cons->role_rh > RSC_ROLE_STARTED) {
retval = crm_strdup_printf("%s (score=%s, %s role=%s, id=%s)",
rsc->id, score, dependents ? "needs" : "with",
role2text(cons->role_rh), cons->id);
} else {
retval = crm_strdup_printf("%s (score=%s, id=%s)",
rsc->id, score, cons->id);
}
free(score);
return retval;
}
static void
colocations_xml_node(pcmk__output_t *out, pe_resource_t *rsc,
pcmk__colocation_t *cons) {
char *score = NULL;
xmlNodePtr node = NULL;
score = score2char(cons->score);
node = pcmk__output_create_xml_node(out, XML_CONS_TAG_RSC_DEPEND,
"id", cons->id,
"rsc", cons->rsc_lh->id,
"with-rsc", cons->rsc_rh->id,
"score", score,
NULL);
if (cons->node_attribute) {
xmlSetProp(node, (pcmkXmlStr) "node-attribute", (pcmkXmlStr) cons->node_attribute);
}
if (cons->role_lh != RSC_ROLE_UNKNOWN) {
xmlSetProp(node, (pcmkXmlStr) "rsc-role", (pcmkXmlStr) role2text(cons->role_lh));
}
if (cons->role_rh != RSC_ROLE_UNKNOWN) {
xmlSetProp(node, (pcmkXmlStr) "with-rsc-role", (pcmkXmlStr) role2text(cons->role_rh));
}
free(score);
}
static int
do_locations_list_xml(pcmk__output_t *out, pe_resource_t *rsc, bool add_header)
{
GList *lpc = NULL;
GList *list = rsc->rsc_location;
int rc = pcmk_rc_no_output;
for (lpc = list; lpc != NULL; lpc = lpc->next) {
pe__location_t *cons = lpc->data;
GList *lpc2 = NULL;
for (lpc2 = cons->node_list_rh; lpc2 != NULL; lpc2 = lpc2->next) {
pe_node_t *node = (pe_node_t *) lpc2->data;
char *score = score2char(node->weight);
if (add_header) {
PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "locations");
}
pcmk__output_create_xml_node(out, XML_CONS_TAG_RSC_LOCATION,
"node", node->details->uname,
"rsc", rsc->id,
"id", cons->id,
"score", score,
NULL);
free(score);
}
}
if (add_header) {
PCMK__OUTPUT_LIST_FOOTER(out, rc);
}
return rc;
}
PCMK__OUTPUT_ARGS("rsc-is-colocated-with-list", "pe_resource_t *", "gboolean")
static int
rsc_is_colocated_with_list(pcmk__output_t *out, va_list args) {
pe_resource_t *rsc = va_arg(args, pe_resource_t *);
gboolean recursive = va_arg(args, gboolean);
int rc = pcmk_rc_no_output;
if (pcmk_is_set(rsc->flags, pe_rsc_allocating)) {
return rc;
}
pe__set_resource_flags(rsc, pe_rsc_allocating);
for (GList *lpc = rsc->rsc_cons; lpc != NULL; lpc = lpc->next) {
pcmk__colocation_t *cons = (pcmk__colocation_t *) lpc->data;
char *hdr = NULL;
PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Resources %s is colocated with", rsc->id);
if (pcmk_is_set(cons->rsc_rh->flags, pe_rsc_allocating)) {
out->list_item(out, NULL, "%s (id=%s - loop)", cons->rsc_rh->id, cons->id);
continue;
}
hdr = colocations_header(cons->rsc_rh, cons, FALSE);
out->list_item(out, NULL, "%s", hdr);
free(hdr);
/* Empty list header just for indentation of information about this resource. */
out->begin_list(out, NULL, NULL, NULL);
out->message(out, "locations-list", cons->rsc_rh);
if (recursive) {
out->message(out, "rsc-is-colocated-with-list", cons->rsc_rh, recursive);
}
out->end_list(out);
}
PCMK__OUTPUT_LIST_FOOTER(out, rc);
return rc;
}
PCMK__OUTPUT_ARGS("rsc-is-colocated-with-list", "pe_resource_t *", "gboolean")
static int
rsc_is_colocated_with_list_xml(pcmk__output_t *out, va_list args) {
pe_resource_t *rsc = va_arg(args, pe_resource_t *);
gboolean recursive = va_arg(args, gboolean);
int rc = pcmk_rc_no_output;
if (pcmk_is_set(rsc->flags, pe_rsc_allocating)) {
return rc;
}
pe__set_resource_flags(rsc, pe_rsc_allocating);
for (GList *lpc = rsc->rsc_cons; lpc != NULL; lpc = lpc->next) {
pcmk__colocation_t *cons = (pcmk__colocation_t *) lpc->data;
if (pcmk_is_set(cons->rsc_rh->flags, pe_rsc_allocating)) {
colocations_xml_node(out, cons->rsc_rh, cons);
continue;
}
colocations_xml_node(out, cons->rsc_rh, cons);
do_locations_list_xml(out, cons->rsc_rh, false);
if (recursive) {
out->message(out, "rsc-is-colocated-with-list", cons->rsc_rh, recursive);
}
}
return rc;
}
PCMK__OUTPUT_ARGS("rscs-colocated-with-list", "pe_resource_t *", "gboolean")
static int
rscs_colocated_with_list(pcmk__output_t *out, va_list args) {
pe_resource_t *rsc = va_arg(args, pe_resource_t *);
gboolean recursive = va_arg(args, gboolean);
int rc = pcmk_rc_no_output;
if (pcmk_is_set(rsc->flags, pe_rsc_allocating)) {
return rc;
}
pe__set_resource_flags(rsc, pe_rsc_allocating);
for (GList *lpc = rsc->rsc_cons_lhs; lpc != NULL; lpc = lpc->next) {
pcmk__colocation_t *cons = (pcmk__colocation_t *) lpc->data;
char *hdr = NULL;
PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Resources colocated with %s", rsc->id);
if (pcmk_is_set(cons->rsc_lh->flags, pe_rsc_allocating)) {
out->list_item(out, NULL, "%s (id=%s - loop)", cons->rsc_lh->id, cons->id);
continue;
}
hdr = colocations_header(cons->rsc_lh, cons, TRUE);
out->list_item(out, NULL, "%s", hdr);
free(hdr);
/* Empty list header just for indentation of information about this resource. */
out->begin_list(out, NULL, NULL, NULL);
out->message(out, "locations-list", cons->rsc_lh);
if (recursive) {
out->message(out, "rscs-colocated-with-list", cons->rsc_lh, recursive);
}
out->end_list(out);
}
PCMK__OUTPUT_LIST_FOOTER(out, rc);
return rc;
}
PCMK__OUTPUT_ARGS("rscs-colocated-with-list", "pe_resource_t *", "gboolean")
static int
rscs_colocated_with_list_xml(pcmk__output_t *out, va_list args) {
pe_resource_t *rsc = va_arg(args, pe_resource_t *);
gboolean recursive = va_arg(args, gboolean);
int rc = pcmk_rc_no_output;
if (pcmk_is_set(rsc->flags, pe_rsc_allocating)) {
return rc;
}
pe__set_resource_flags(rsc, pe_rsc_allocating);
for (GList *lpc = rsc->rsc_cons_lhs; lpc != NULL; lpc = lpc->next) {
pcmk__colocation_t *cons = (pcmk__colocation_t *) lpc->data;
if (pcmk_is_set(cons->rsc_lh->flags, pe_rsc_allocating)) {
colocations_xml_node(out, cons->rsc_lh, cons);
continue;
}
colocations_xml_node(out, cons->rsc_lh, cons);
do_locations_list_xml(out, cons->rsc_lh, false);
if (recursive) {
out->message(out, "rscs-colocated-with-list", cons->rsc_lh, recursive);
}
}
return rc;
}
PCMK__OUTPUT_ARGS("locations-list", "pe_resource_t *")
static int
locations_list(pcmk__output_t *out, va_list args) {
pe_resource_t *rsc = va_arg(args, pe_resource_t *);
GList *lpc = NULL;
GList *list = rsc->rsc_location;
int rc = pcmk_rc_no_output;
for (lpc = list; lpc != NULL; lpc = lpc->next) {
pe__location_t *cons = lpc->data;
GList *lpc2 = NULL;
for (lpc2 = cons->node_list_rh; lpc2 != NULL; lpc2 = lpc2->next) {
pe_node_t *node = (pe_node_t *) lpc2->data;
char *score = score2char(node->weight);
PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Locations");
out->list_item(out, NULL, "Node %s (score=%s, id=%s, rsc=%s)",
node->details->uname, score, cons->id, rsc->id);
free(score);
}
}
PCMK__OUTPUT_LIST_FOOTER(out, rc);
return rc;
}
PCMK__OUTPUT_ARGS("locations-list", "pe_resource_t *")
static int
locations_list_xml(pcmk__output_t *out, va_list args) {
pe_resource_t *rsc = va_arg(args, pe_resource_t *);
return do_locations_list_xml(out, rsc, true);
}
PCMK__OUTPUT_ARGS("stacks-constraints", "pe_resource_t *", "pe_working_set_t *", "gboolean")
static int
stacks_and_constraints(pcmk__output_t *out, va_list args) {
pe_resource_t *rsc = va_arg(args, pe_resource_t *);
pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
gboolean recursive = va_arg(args, gboolean);
xmlNodePtr cib_constraints = get_object_root(XML_CIB_TAG_CONSTRAINTS,
data_set->input);
unpack_constraints(cib_constraints, data_set);
// Constraints apply to group/clone, not member/instance
rsc = uber_parent(rsc);
out->message(out, "locations-list", rsc);
pe__clear_resource_flags_on_all(data_set, pe_rsc_allocating);
out->message(out, "rscs-colocated-with-list", rsc, recursive);
pe__clear_resource_flags_on_all(data_set, pe_rsc_allocating);
out->message(out, "rsc-is-colocated-with-list", rsc, recursive);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("stacks-constraints", "pe_resource_t *", "pe_working_set_t *", "gboolean")
static int
stacks_and_constraints_xml(pcmk__output_t *out, va_list args) {
pe_resource_t *rsc = va_arg(args, pe_resource_t *);
pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
gboolean recursive = va_arg(args, gboolean);
xmlNodePtr cib_constraints = get_object_root(XML_CIB_TAG_CONSTRAINTS,
data_set->input);
unpack_constraints(cib_constraints, data_set);
// Constraints apply to group/clone, not member/instance
rsc = uber_parent(rsc);
pcmk__output_xml_create_parent(out, "constraints", NULL);
do_locations_list_xml(out, rsc, false);
pe__clear_resource_flags_on_all(data_set, pe_rsc_allocating);
out->message(out, "rscs-colocated-with-list", rsc, recursive);
pe__clear_resource_flags_on_all(data_set, pe_rsc_allocating);
out->message(out, "rsc-is-colocated-with-list", rsc, recursive);
pcmk__output_xml_pop_parent(out);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("health", "const char *", "const char *", "const char *", "const char *")
static int
health_text(pcmk__output_t *out, va_list args)
{
const char *sys_from = va_arg(args, const char *);
const char *host_from = va_arg(args, const char *);
const char *fsa_state = va_arg(args, const char *);
const char *result = va_arg(args, const char *);
if (!out->is_quiet(out)) {
out->info(out, "Status of %s@%s: %s (%s)", crm_str(sys_from),
crm_str(host_from), crm_str(fsa_state), crm_str(result));
} else if (fsa_state != NULL) {
out->info(out, "%s", fsa_state);
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("health", "const char *", "const char *", "const char *", "const char *")
static int
health_xml(pcmk__output_t *out, va_list args)
{
const char *sys_from = va_arg(args, const char *);
const char *host_from = va_arg(args, const char *);
const char *fsa_state = va_arg(args, const char *);
const char *result = va_arg(args, const char *);
pcmk__output_create_xml_node(out, crm_str(sys_from),
"node_name", crm_str(host_from),
"state", crm_str(fsa_state),
"result", crm_str(result),
NULL);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("pacemakerd-health", "const char *", "const char *", "const char *")
static int
pacemakerd_health_text(pcmk__output_t *out, va_list args)
{
const char *sys_from = va_arg(args, const char *);
const char *state = va_arg(args, const char *);
const char *last_updated = va_arg(args, const char *);
if (!out->is_quiet(out)) {
out->info(out, "Status of %s: '%s' %s %s", crm_str(sys_from),
crm_str(state), (!pcmk__str_empty(last_updated))?
"last updated":"", crm_str(last_updated));
} else {
out->info(out, "%s", crm_str(state));
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("pacemakerd-health", "const char *", "const char *", "const char *")
static int
pacemakerd_health_xml(pcmk__output_t *out, va_list args)
{
const char *sys_from = va_arg(args, const char *);
const char *state = va_arg(args, const char *);
const char *last_updated = va_arg(args, const char *);
pcmk__output_create_xml_node(out, crm_str(sys_from),
"state", crm_str(state),
"last_updated", crm_str(last_updated),
NULL);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("dc", "const char *")
static int
dc_text(pcmk__output_t *out, va_list args)
{
const char *dc = va_arg(args, const char *);
if (!out->is_quiet(out)) {
out->info(out, "Designated Controller is: %s", crm_str(dc));
} else if (dc != NULL) {
out->info(out, "%s", dc);
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("dc", "const char *")
static int
dc_xml(pcmk__output_t *out, va_list args)
{
const char *dc = va_arg(args, const char *);
pcmk__output_create_xml_node(out, "dc",
"node_name", crm_str(dc),
NULL);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("crmadmin-node-list", "xmlNodePtr", "gboolean")
static int
crmadmin_node_list(pcmk__output_t *out, va_list args)
{
xmlNodePtr xml_node = va_arg(args, xmlNodePtr);
gboolean BASH_EXPORT = va_arg(args, gboolean);
int found = 0;
xmlNode *node = NULL;
xmlNode *nodes = get_object_root(XML_CIB_TAG_NODES, xml_node);
out->begin_list(out, NULL, NULL, "nodes");
for (node = first_named_child(nodes, XML_CIB_TAG_NODE); node != NULL;
node = crm_next_same_xml(node)) {
const char *node_type = BASH_EXPORT ? NULL :
crm_element_value(node, XML_ATTR_TYPE);
out->message(out, "crmadmin-node", node_type,
crm_str(crm_element_value(node, XML_ATTR_UNAME)),
crm_str(crm_element_value(node, XML_ATTR_ID)),
BASH_EXPORT);
found++;
}
// @TODO List Pacemaker Remote nodes that don't have a <node> entry
out->end_list(out);
if (found == 0) {
out->info(out, "No nodes configured");
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("crmadmin-node", "const char *", "const char *", "const char *", "gboolean")
static int
crmadmin_node_text(pcmk__output_t *out, va_list args)
{
const char *type = va_arg(args, const char *);
const char *name = va_arg(args, const char *);
const char *id = va_arg(args, const char *);
gboolean BASH_EXPORT = va_arg(args, gboolean);
if (BASH_EXPORT) {
out->info(out, "export %s=%s", crm_str(name), crm_str(id));
} else {
out->info(out, "%s node: %s (%s)", type ? type : "member",
crm_str(name), crm_str(id));
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("crmadmin-node", "const char *", "const char *", "const char *", "gboolean")
static int
crmadmin_node_xml(pcmk__output_t *out, va_list args)
{
const char *type = va_arg(args, const char *);
const char *name = va_arg(args, const char *);
const char *id = va_arg(args, const char *);
pcmk__output_create_xml_node(out, "node",
"type", type ? type : "member",
"name", crm_str(name),
"id", crm_str(id),
NULL);
return pcmk_rc_ok;
}
+PCMK__OUTPUT_ARGS("digests", "pe_resource_t *", "pe_node_t *", "const char *",
+ "guint", "op_digest_cache_t *")
+static int
+digests_text(pcmk__output_t *out, va_list args)
+{
+ pe_resource_t *rsc = va_arg(args, pe_resource_t *);
+ pe_node_t *node = va_arg(args, pe_node_t *);
+ const char *task = va_arg(args, const char *);
+ guint interval_ms = va_arg(args, guint);
+ op_digest_cache_t *digests = va_arg(args, op_digest_cache_t *);
+
+ char *action_desc = NULL;
+ const char *rsc_desc = "unknown resource";
+ const char *node_desc = "unknown node";
+
+ if (interval_ms != 0) {
+ action_desc = crm_strdup_printf("%ums-interval %s action", interval_ms,
+ ((task == NULL)? "unknown" : task));
+ } else if (pcmk__str_eq(task, "monitor", pcmk__str_none)) {
+ action_desc = strdup("probe action");
+ } else {
+ action_desc = crm_strdup_printf("%s action",
+ ((task == NULL)? "unknown" : task));
+ }
+ if ((rsc != NULL) && (rsc->id != NULL)) {
+ rsc_desc = rsc->id;
+ }
+ if ((node != NULL) && (node->details->uname != NULL)) {
+ node_desc = node->details->uname;
+ }
+ out->begin_list(out, NULL, NULL, "Digests for %s %s on %s",
+ rsc_desc, action_desc, node_desc);
+ free(action_desc);
+
+ if (digests == NULL) {
+ out->list_item(out, NULL, "none");
+ out->end_list(out);
+ return pcmk_rc_ok;
+ }
+ if (digests->digest_all_calc != NULL) {
+ out->list_item(out, NULL, "%s (all parameters)",
+ digests->digest_all_calc);
+ }
+ if (digests->digest_secure_calc != NULL) {
+ out->list_item(out, NULL, "%s (non-private parameters)",
+ digests->digest_secure_calc);
+ }
+ if (digests->digest_restart_calc != NULL) {
+ out->list_item(out, NULL, "%s (non-reloadable parameters)",
+ digests->digest_restart_calc);
+ }
+ out->end_list(out);
+ return pcmk_rc_ok;
+}
+
+static void
+add_digest_xml(xmlNode *parent, const char *type, const char *digest,
+ xmlNode *digest_source)
+{
+ if (digest != NULL) {
+ xmlNodePtr digest_xml = create_xml_node(parent, "digest");
+
+ crm_xml_add(digest_xml, "type", ((type == NULL)? "unspecified" : type));
+ crm_xml_add(digest_xml, "hash", digest);
+ if (digest_source != NULL) {
+ add_node_copy(digest_xml, digest_source);
+ }
+ }
+}
+
+PCMK__OUTPUT_ARGS("digests", "pe_resource_t *", "pe_node_t *", "const char *",
+ "guint", "op_digest_cache_t *")
+static int
+digests_xml(pcmk__output_t *out, va_list args)
+{
+ pe_resource_t *rsc = va_arg(args, pe_resource_t *);
+ pe_node_t *node = va_arg(args, pe_node_t *);
+ const char *task = va_arg(args, const char *);
+ guint interval_ms = va_arg(args, guint);
+ op_digest_cache_t *digests = va_arg(args, op_digest_cache_t *);
+
+ char *interval_s = crm_strdup_printf("%ums", interval_ms);
+ xmlNode *xml = NULL;
+
+ xml = pcmk__output_create_xml_node(out, "digests",
+ "resource", crm_str(rsc->id),
+ "node", crm_str(node->details->uname),
+ "task", crm_str(task),
+ "interval", interval_s,
+ NULL);
+ free(interval_s);
+ if (digests != NULL) {
+ add_digest_xml(xml, "all", digests->digest_all_calc,
+ digests->params_all);
+ add_digest_xml(xml, "nonprivate", digests->digest_secure_calc,
+ digests->params_secure);
+ add_digest_xml(xml, "nonreloadable", digests->digest_restart_calc,
+ digests->params_restart);
+ }
+ return pcmk_rc_ok;
+}
+
static pcmk__message_entry_t fmt_functions[] = {
{ "rsc-is-colocated-with-list", "default", rsc_is_colocated_with_list },
{ "rsc-is-colocated-with-list", "xml", rsc_is_colocated_with_list_xml },
{ "rscs-colocated-with-list", "default", rscs_colocated_with_list },
{ "rscs-colocated-with-list", "xml", rscs_colocated_with_list_xml },
{ "locations-list", "default", locations_list },
{ "locations-list", "xml", locations_list_xml },
{ "stacks-constraints", "default", stacks_and_constraints },
{ "stacks-constraints", "xml", stacks_and_constraints_xml },
{ "health", "default", health_text },
{ "health", "xml", health_xml },
{ "pacemakerd-health", "default", pacemakerd_health_text },
{ "pacemakerd-health", "xml", pacemakerd_health_xml },
{ "dc", "default", dc_text },
{ "dc", "xml", dc_xml },
{ "crmadmin-node-list", "default", crmadmin_node_list },
{ "crmadmin-node", "default", crmadmin_node_text },
{ "crmadmin-node", "xml", crmadmin_node_xml },
+ { "digests", "default", digests_text },
+ { "digests", "xml", digests_xml },
{ NULL, NULL, NULL }
};
void
pcmk__register_lib_messages(pcmk__output_t *out) {
pcmk__register_messages(out, fmt_functions);
}
diff --git a/lib/pacemaker/pcmk_resource.c b/lib/pacemaker/pcmk_resource.c
index 05614fcd2e..197edf8e93 100644
--- a/lib/pacemaker/pcmk_resource.c
+++ b/lib/pacemaker/pcmk_resource.c
@@ -1,21 +1,140 @@
/*
* Copyright 2021 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU General Public License version 2
* or later (GPLv2+) WITHOUT ANY WARRANTY.
*/
#include <crm_internal.h>
+#include <errno.h>
#include <glib.h>
#include <libxml/tree.h>
#include <crm/common/mainloop.h>
#include <crm/common/results.h>
#include <crm/common/output_internal.h>
#include <crm/pengine/internal.h>
#include <pacemaker.h>
#include <pacemaker-internal.h>
+
+// Search path for resource operation history (takes node name and resource ID)
+#define XPATH_OP_HISTORY "//" XML_CIB_TAG_STATUS \
+ "/" XML_CIB_TAG_STATE "[@" XML_ATTR_UNAME "='%s']" \
+ "/" XML_CIB_TAG_LRM "/" XML_LRM_TAG_RESOURCES \
+ "/" XML_LRM_TAG_RESOURCE "[@" XML_ATTR_ID "='%s']"
+
+static xmlNode *
+best_op(pe_resource_t *rsc, pe_node_t *node, pe_working_set_t *data_set)
+{
+ char *xpath = NULL;
+ xmlNode *history = NULL;
+ xmlNode *best = NULL;
+
+ // Find node's resource history
+ xpath = crm_strdup_printf(XPATH_OP_HISTORY, node->details->uname, rsc->id);
+ history = get_xpath_object(xpath, data_set->input, LOG_NEVER);
+ free(xpath);
+
+ // Examine each history entry
+ for (xmlNode *lrm_rsc_op = first_named_child(history, XML_LRM_TAG_RSC_OP);
+ lrm_rsc_op != NULL; lrm_rsc_op = crm_next_same_xml(lrm_rsc_op)) {
+
+ const char *digest = crm_element_value(lrm_rsc_op,
+ XML_LRM_ATTR_RESTART_DIGEST);
+ guint interval_ms = 0;
+
+ crm_element_value_ms(lrm_rsc_op, XML_LRM_ATTR_INTERVAL, &interval_ms);
+
+ if (pcmk__ends_with(ID(lrm_rsc_op), "_last_failure_0")
+ || (interval_ms != 0)) {
+
+ // Only use last failure or recurring op if nothing else available
+ if (best == NULL) {
+ best = lrm_rsc_op;
+ }
+ continue;
+ }
+
+ best = lrm_rsc_op;
+ if (digest != NULL) {
+ // Any non-recurring action with a restart digest is sufficient
+ break;
+ }
+ }
+ return best;
+}
+
+/*!
+ * \internal
+ * \brief Calculate and output resource operation digests
+ *
+ * \param[in] out Output object
+ * \param[in] rsc Resource to calculate digests for
+ * \param[in] node Node whose operation history should be used
+ * \param[in] overrides Hash table of configuration parameters to override
+ * \param[in] data_set Cluster working set (with status)
+ *
+ * \return Standard Pacemaker return code
+ */
+int
+pcmk__resource_digests(pcmk__output_t *out, pe_resource_t *rsc,
+ pe_node_t *node, GHashTable *overrides,
+ pe_working_set_t *data_set)
+{
+ const char *task = NULL;
+ xmlNode *xml_op = NULL;
+ op_digest_cache_t *digests = NULL;
+ guint interval_ms = 0;
+ int rc = pcmk_rc_ok;
+
+ if ((out == NULL) || (rsc == NULL) || (node == NULL) || (data_set == NULL)) {
+ return EINVAL;
+ }
+ if (rsc->variant != pe_native) {
+ // Only primitives get operation digests
+ return EOPNOTSUPP;
+ }
+
+ // Find XML of operation history to use
+ xml_op = best_op(rsc, node, data_set);
+
+ // Generate an operation key
+ if (xml_op != NULL) {
+ task = crm_element_value(xml_op, XML_LRM_ATTR_TASK);
+ crm_element_value_ms(xml_op, XML_LRM_ATTR_INTERVAL_MS, &interval_ms);
+ }
+ if (task == NULL) { // Assume start if no history is available
+ task = RSC_START;
+ interval_ms = 0;
+ }
+
+ // Calculate and show digests
+ digests = pe__calculate_digests(rsc, task, &interval_ms, node, xml_op,
+ overrides, true, data_set);
+ rc = out->message(out, "digests", rsc, node, task, interval_ms, digests);
+
+ pe__free_digests(digests);
+ return rc;
+}
+
+int
+pcmk_resource_digests(xmlNodePtr *xml, pe_resource_t *rsc,
+ pe_node_t *node, GHashTable *overrides,
+ pe_working_set_t *data_set)
+{
+ pcmk__output_t *out = NULL;
+ int rc = pcmk_rc_ok;
+
+ rc = pcmk__out_prologue(&out, xml);
+ if (rc != pcmk_rc_ok) {
+ return rc;
+ }
+ pcmk__register_lib_messages(out);
+ rc = pcmk__resource_digests(out, rsc, node, overrides, data_set);
+ pcmk__out_epilogue(out, xml, rc);
+ return rc;
+}
diff --git a/xml/Makefile.am b/xml/Makefile.am
index e7b9a512d0..cb6cfa0dde 100644
--- a/xml/Makefile.am
+++ b/xml/Makefile.am
@@ -1,268 +1,274 @@
#
-# Copyright 2004-2019 the Pacemaker project contributors
+# Copyright 2004-2021 the Pacemaker project contributors
#
# The version control history for this file may have further details.
#
# This source code is licensed under the GNU General Public License version 2
# or later (GPLv2+) WITHOUT ANY WARRANTY.
#
include $(top_srcdir)/mk/common.mk
noarch_pkgconfig_DATA = $(builddir)/pacemaker-schemas.pc
# Pacemaker has 3 schemas: the CIB schema, the API schema (for command-line
# tool XML output), and a legacy schema for crm_mon --as-xml.
#
# See README.md for details on updating CIB schema files (API is similar)
# The CIB and crm_mon schemas are installed directly in CRM_SCHEMA_DIRECTORY
# for historical reasons, while the API schema is installed in a subdirectory.
APIdir = $(CRM_SCHEMA_DIRECTORY)/api
CIBdir = $(CRM_SCHEMA_DIRECTORY)
MONdir = $(CRM_SCHEMA_DIRECTORY)
# Extract a sorted list of available numeric schema versions
# from filenames like NAME-MAJOR[.MINOR][.MINOR-MINOR].rng
numeric_versions = $(shell ls -1 $(1) \
| sed -n -e 's/^.*-\([0-9][0-9.]*\).rng$$/\1/p' \
| sort -u -t. -k 1,1n -k 2,2n -k 3,3n)
version_pairs = $(join \
$(1),$(addprefix \
-,$(wordlist \
2,$(words $(1)),$(1) \
) next \
) \
)
version_pairs_last = $(wordlist \
$(words \
$(wordlist \
2,$(1),$(2) \
) \
),$(1),$(2) \
)
# NOTE: All files in API_request_base, CIB_cfg_base, API_base, and CIB_base
# need to start with a unique prefix. These variables all get iterated over
# and globbed, and two files starting with the same prefix will cause
# problems.
# Names of API schemas that form the choices for pacemaker-result content
-API_request_base = command-output crm_mon crm_resource crmadmin stonith_admin version
+API_request_base = command-output \
+ crm_mon \
+ crm_resource \
+ crmadmin \
+ digests \
+ stonith_admin \
+ version
# Names of CIB schemas that form the choices for cib/configuration content
CIB_cfg_base = options nodes resources constraints fencing acls tags alerts
# Names of all schemas (including top level and those included by others)
API_base = $(API_request_base) fence-event generic-list item resources status
CIB_base = cib $(CIB_cfg_base) status score rule nvset
# Static schema files and transforms (only CIB has transforms)
#
# This is more complicated than it should be due to the need to support
# VPATH builds and "make distcheck". We need the absolute paths for reliable
# substitution back and forth, and relative paths for distributed files.
API_abs_files = $(foreach base,$(API_base),$(wildcard $(abs_srcdir)/api/$(base)*.rng))
CIB_abs_files = $(foreach base,$(CIB_base),$(wildcard $(abs_srcdir)/$(base).rng $(abs_srcdir)/$(base)-*.rng))
CIB_abs_xsl = $(abs_srcdir)/upgrade-1.3.xsl \
$(abs_srcdir)/upgrade-2.10.xsl \
$(wildcard $(abs_srcdir)/upgrade-*enter.xsl) \
$(wildcard $(abs_srcdir)/upgrade-*leave.xsl)
MON_abs_files = $(abs_srcdir)/crm_mon.rng
API_files = $(foreach base,$(API_base),$(wildcard $(srcdir)/api/$(base)*.rng))
CIB_files = $(foreach base,$(CIB_base),$(wildcard $(srcdir)/$(base).rng $(srcdir)/$(base)-*.rng))
CIB_xsl = $(srcdir)/upgrade-1.3.xsl \
$(srcdir)/upgrade-2.10.xsl \
$(wildcard $(srcdir)/upgrade-*enter.xsl) \
$(wildcard $(srcdir)/upgrade-*leave.xsl)
MON_files = $(srcdir)/crm_mon.rng
# Sorted lists of all numeric schema versions
API_numeric_versions = $(call numeric_versions,${API_files})
CIB_numeric_versions = $(call numeric_versions,${CIB_files})
# The highest numeric schema version
API_max ?= $(lastword $(API_numeric_versions))
CIB_max ?= $(lastword $(CIB_numeric_versions))
# Sorted lists of all schema versions (including "next")
API_versions = next $(API_numeric_versions)
CIB_versions = next $(CIB_numeric_versions)
# Build tree locations of static schema files and transforms (for VPATH builds)
API_build_copies = $(foreach f,$(API_abs_files),$(subst $(abs_srcdir),$(abs_builddir),$(f)))
CIB_build_copies = $(foreach f,$(CIB_abs_files) $(CIB_abs_xsl),$(subst $(abs_srcdir),$(abs_builddir),$(f)))
MON_build_copies = $(foreach f,$(MON_abs_files),$(subst $(abs_srcdir),$(abs_builddir),$(f)))
# Dynamically generated schema files
API_generated = api/api-result.rng $(foreach base,$(API_versions),api/api-result-$(base).rng)
CIB_generated = pacemaker.rng $(foreach base,$(CIB_versions),pacemaker-$(base).rng) versions.rng
CIB_version_pairs = $(call version_pairs,${CIB_numeric_versions})
CIB_version_pairs_cnt = $(words ${CIB_version_pairs})
CIB_version_pairs_last = $(call version_pairs_last,${CIB_version_pairs_cnt},${CIB_version_pairs})
dist_API_DATA = $(API_files)
dist_CIB_DATA = $(CIB_files) $(CIB_xsl)
dist_MON_DATA = $(MON_files)
nodist_API_DATA = $(API_generated)
nodist_CIB_DATA = $(CIB_generated)
EXTRA_DIST = README.md \
best-match.sh \
cibtr-2.rng \
context-of.xsl \
ocf-meta2man.xsl \
regression.sh \
upgrade-2.10-roundtrip.xsl \
upgrade-detail.xsl \
xslt_cibtr-2.rng \
assets \
test-2 \
test-2-enter \
test-2-leave \
test-2-roundtrip
cib-versions:
@echo "Max: $(CIB_max)"
@echo "Available: $(CIB_versions)"
api-versions:
@echo "Max: $(API_max)"
@echo "Available: $(API_versions)"
# Dynamically generated top-level API schema
api/api-result.rng: api/api-result-$(API_max).rng
$(AM_V_at)$(MKDIR_P) api # might not exist in VPATH build
$(AM_V_SCHEMA)cp $(top_builddir)/xml/$< $@
api/api-result-%.rng: $(API_build_copies) best-match.sh Makefile.am
$(AM_V_at)echo '<?xml version="1.0" encoding="UTF-8"?>' > $@
$(AM_V_at)echo '<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">' >> $@
$(AM_V_at)echo ' <start>' >> $@
$(AM_V_at)echo ' <element name="pacemaker-result">' >> $@
$(AM_V_at)echo ' <attribute name="api-version"> <text /> </attribute>' >> $@
$(AM_V_at)echo ' <attribute name="request"> <text /> </attribute>' >> $@
$(AM_V_at)echo ' <optional>' >> $@
$(AM_V_at)echo ' <choice>' >> $@
$(AM_V_at)for rng in $(API_request_base); do $(srcdir)/best-match.sh api/$$rng $(*) $(@) " " || :; done
$(AM_V_at)echo ' </choice>' >> $@
$(AM_V_at)echo ' </optional>' >> $@
$(AM_V_at)$(srcdir)/best-match.sh api/status $(*) $(@) " " || :
$(AM_V_at)echo ' </element>' >> $@
$(AM_V_at)echo ' </start>' >> $@
$(AM_V_SCHEMA)echo '</grammar>' >> $@
# Dynamically generated top-level CIB schema
pacemaker.rng: pacemaker-$(CIB_max).rng
$(AM_V_SCHEMA)cp $(top_builddir)/xml/$< $@
pacemaker-%.rng: $(CIB_build_copies) best-match.sh Makefile.am
$(AM_V_at)echo '<?xml version="1.0" encoding="UTF-8"?>' > $@
$(AM_V_at)echo '<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">' >> $@
$(AM_V_at)echo ' <start>' >> $@
$(AM_V_at)echo ' <element name="cib">' >> $@
$(AM_V_at)$(srcdir)/best-match.sh cib $(*) $(@) " "
$(AM_V_at)echo ' <element name="configuration">' >> $@
$(AM_V_at)echo ' <interleave>' >> $@
$(AM_V_at)for rng in $(CIB_cfg_base); do $(srcdir)/best-match.sh $$rng $(*) $(@) " " || :; done
$(AM_V_at)echo ' </interleave>' >> $@
$(AM_V_at)echo ' </element>' >> $@
$(AM_V_at)echo ' <optional>' >> $@
$(AM_V_at)echo ' <element name="status">' >> $@
$(AM_V_at)$(srcdir)/best-match.sh status $(*) $(@) " "
$(AM_V_at)echo ' </element>' >> $@
$(AM_V_at)echo ' </optional>' >> $@
$(AM_V_at)echo ' </element>' >> $@
$(AM_V_at)echo ' </start>' >> $@
$(AM_V_SCHEMA)echo '</grammar>' >> $@
# Dynamically generated CIB schema listing all pacemaker versions
versions.rng: Makefile.am
$(AM_V_at)echo '<?xml version="1.0" encoding="UTF-8"?>' > $@
$(AM_V_at)echo '<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">' >> $@
$(AM_V_at)echo ' <start>' >> $@
$(AM_V_at)echo ' <interleave>' >> $@
$(AM_V_at)echo ' <optional>' >> $@
$(AM_V_at)echo ' <attribute name="validate-with">' >> $@
$(AM_V_at)echo ' <choice>' >> $@
$(AM_V_at)echo ' <value>none</value>' >> $@
$(AM_V_at)echo ' <value>pacemaker-0.6</value>' >> $@
$(AM_V_at)echo ' <value>transitional-0.6</value>' >> $@
$(AM_V_at)echo ' <value>pacemaker-0.7</value>' >> $@
$(AM_V_at)echo ' <value>pacemaker-1.1</value>' >> $@
$(AM_V_at)for rng in $(CIB_versions); do echo " <value>pacemaker-$$rng</value>" >> $@; done
$(AM_V_at)echo ' </choice>' >> $@
$(AM_V_at)echo ' </attribute>' >> $@
$(AM_V_at)echo ' </optional>' >> $@
$(AM_V_at)echo ' <attribute name="admin_epoch"><data type="nonNegativeInteger"/></attribute>' >> $@
$(AM_V_at)echo ' <attribute name="epoch"><data type="nonNegativeInteger"/></attribute>' >> $@
$(AM_V_at)echo ' <attribute name="num_updates"><data type="nonNegativeInteger"/></attribute>' >> $@
$(AM_V_at)echo ' </interleave>' >> $@
$(AM_V_at)echo ' </start>' >> $@
$(AM_V_SCHEMA)echo '</grammar>' >> $@
# diff fails with ec=2 if no predecessor is found;
# this uses '=' GNU extension to sed, if that's not available,
# one can use: hline=`echo "$${p}" | grep -Fn "$${hunk}" | cut -d: -f1`;
# XXX: use line information from hunk to avoid "not detected" for ambiguity
version_diff = \
@for p in $(1); do \
set `echo "$${p}" | tr '-' ' '`; \
echo "\#\#\# *-$$2.rng vs. predecessor"; \
for v in *-$$2.rng; do \
echo "\#\#\#\# $${v} vs. predecessor"; b=`echo "$${v}" | cut -d- -f1`; \
old=`./best-match.sh $${b} $$1`; \
p=`diff -u "$${old}" "$${v}" 2>/dev/null`; \
case $$? in \
1) echo "$${p}" | sed -n -e '/^@@ /!d;=;p' \
-e ':l;n;/^\([- ]\|+.*<[^ />]\+\([^/>]\+="ID\|>$$\)\)/bl;s/^[+ ]\(.*\)/\1/p' \
| while read hline; do \
read h && read i || break; \
iline=`grep -Fn "$${i}" "$${v}" | cut -d: -f1`; \
ctxt="(not detected)"; \
if test `echo "$${iline}" | wc -l` -eq 1; then \
ctxt=`{ sed -n -e "1,$$(($${iline}-1))p" "$${v}"; \
echo "<inject id=\"GOAL\"/>$${i}"; \
sed -n -e "$$(($${iline}+1)),$$ p" "$${v}"; \
} | $(XSLTPROC) --param skip 1 context-of.xsl -`; \
fi; \
echo "$${p}" | sed -n -e "$$(($${hline}-2)),$${hline}!d" \
-e '/^\(+++\|---\)/p'; \
echo "$${h} context: $${ctxt}"; \
echo "$${p}" | sed -n -e "1,$${hline}d" \
-e '/^\(---\|@@ \)/be;p;d;:e;n;be'; \
done; \
;; \
2) echo "\#\#\#\#\# $${v} has no predecessor";; \
esac; \
done; \
done
diff: best-match.sh
@echo "# Comparing changes in + since $(CIB_max)"
$(call version_diff,${CIB_version_pairs_last})
fulldiff: best-match.sh
@echo "# Comparing all changes across all the subsequent increments"
$(call version_diff,${CIB_version_pairs})
CLEANFILES = $(API_generated) $(CIB_generated)
clean-local:
if [ "x$(srcdir)" != "x$(builddir)" ]; then \
rm -f $(API_build_copies) $(CIB_build_copies) $(MON_build_copies); \
fi
# Enable ability to use $@ in prerequisite
.SECONDEXPANSION:
# For VPATH builds, copy the static schema files into the build tree
$(API_build_copies) $(CIB_build_copies) $(MON_build_copies): $$(subst $(abs_builddir),$(srcdir),$$(@))
$(AM_V_GEN)if [ "x$(srcdir)" != "x$(builddir)" ]; then \
$(MKDIR_P) "$(dir $(@))"; \
cp "$(<)" "$(@)"; \
fi
diff --git a/xml/api/digests-2.6.rng b/xml/api/digests-2.6.rng
new file mode 100644
index 0000000000..7e843d41e9
--- /dev/null
+++ b/xml/api/digests-2.6.rng
@@ -0,0 +1,33 @@
+<?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-digests"/>
+ </start>
+
+ <define name="element-digests">
+ <attribute name="resource"> <text/> </attribute>
+ <attribute name="node"> <text/> </attribute>
+ <attribute name="task"> <text/> </attribute>
+ <attribute name="interval"> <text/> </attribute>
+ <zeroOrMore>
+ <ref name="element-digest"/>
+ </zeroOrMore>
+ </define>
+
+ <define name="element-digest">
+ <attribute name="type"> <text/> </attribute>
+ <attribute name="hash"> <text/> </attribute>
+ <optional>
+ <element name="parameters">
+ <zeroOrMore>
+ <attribute>
+ <anyName/>
+ <text/>
+ </attribute>
+ </zeroOrMore>
+ </element>
+ </optional>
+ </define>
+</grammar>
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, Sep 5, 9:30 AM (12 h, 30 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2298602
Default Alt Text
(51 KB)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment