diff --git a/xml/Makefile.am b/xml/Makefile.am
index e56c9e0e97..716ef98def 100644
--- a/xml/Makefile.am
+++ b/xml/Makefile.am
@@ -1,298 +1,282 @@
#
# 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 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)
basexsltdir = $(CRM_SCHEMA_DIRECTORY)/base
dist_basexslt_DATA = $(srcdir)/base/access-render-2.xsl
# 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)
# @COMPAT: pacemaker-next is deprecated since 2.1.5
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_attribute \
crm_error \
crm_mon \
crm_node \
crm_resource \
crm_rule \
crm_shadow \
crm_simulate \
crm_ticket \
crmadmin \
digests \
pacemakerd \
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) \
any-element \
failure \
fence-event \
generic-list \
instruction \
item \
node-attrs \
node-history \
nodes \
ocf-ra \
options \
patchset \
resources \
status \
subprocess-output \
ticket
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})
MON_numeric_versions = $(call numeric_versions,$(wildcard $(srcdir)/api/crm_mon*.rng))
# The highest numeric schema version
API_max ?= $(lastword $(API_numeric_versions))
CIB_max ?= $(lastword $(CIB_numeric_versions))
MON_max ?= $(lastword $(MON_numeric_versions))
# Sorted lists of all schema versions (including "next")
# @COMPAT: pacemaker-next is deprecated since 2.1.5
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
MON_generated = crm_mon.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)
nodist_API_DATA = $(API_generated)
nodist_CIB_DATA = $(CIB_generated)
nodist_MON_DATA = $(MON_generated)
EXTRA_DIST = README.md \
cibtr-2.rng \
context-of.xsl \
rng-helper \
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
.PHONY: cib-versions
cib-versions:
@echo "Max: $(CIB_max)"
@echo "Available: $(CIB_versions)"
.PHONY: api-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) rng-helper Makefile.am
$(AM_V_SCHEMA)$(builddir)/rng-helper build_api_rng "$@" "$*" \
$(API_request_base)
crm_mon.rng: api/crm_mon-$(MON_max).rng
$(AM_V_at)echo '' > $@
$(AM_V_at)echo '> $@
$(AM_V_at)echo ' datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">' >> $@
$(AM_V_at)echo ' ' >> $@
$(AM_V_at)echo ' ' >> $@
$(AM_V_at)echo ' ' >> $@
$(AM_V_at)echo ' ' >> $@
$(AM_V_at)echo ' ' >> $@
$(AM_V_at)echo ' ' >> $@
$(AM_V_at)echo ' ' >> $@
$(AM_V_at)echo ' ' >> $@
$(AM_V_at)echo ' ' >> $@
$(AM_V_SCHEMA)echo '' >> $@
# Dynamically generated top-level CIB schema
pacemaker.rng: pacemaker-$(CIB_max).rng
$(AM_V_SCHEMA)cp $(top_builddir)/xml/$< $@
pacemaker-%.rng: $(CIB_build_copies) rng-helper Makefile.am
- $(AM_V_at)echo '' > $@
- $(AM_V_at)echo '' >> $@
- $(AM_V_at)echo ' ' >> $@
- $(AM_V_at)echo ' ' >> $@
- $(AM_V_at)$(builddir)/rng-helper match cib $(*) $(@) " "
- $(AM_V_at)echo ' ' >> $@
- $(AM_V_at)echo ' ' >> $@
- $(AM_V_at)for rng in $(CIB_cfg_base); do $(builddir)/rng-helper match $$rng $(*) $(@) " " || :; done
- $(AM_V_at)echo ' ' >> $@
- $(AM_V_at)echo ' ' >> $@
- $(AM_V_at)echo ' ' >> $@
- $(AM_V_at)echo ' ' >> $@
- $(AM_V_at)$(builddir)/rng-helper match status $(*) $(@) " "
- $(AM_V_at)echo ' ' >> $@
- $(AM_V_at)echo ' ' >> $@
- $(AM_V_at)echo ' ' >> $@
- $(AM_V_at)echo ' ' >> $@
- $(AM_V_SCHEMA)echo '' >> $@
+ $(AM_V_SCHEMA)$(builddir)/rng-helper build_cib_rng "$@" "$*" \
+ $(CIB_cfg_base)
# Dynamically generated CIB schema listing all pacemaker versions
versions.rng: pacemaker-$(CIB_max).rng Makefile.am
$(AM_V_at)echo '' > $@
$(AM_V_at)echo '' >> $@
$(AM_V_at)echo ' ' >> $@
$(AM_V_at)echo ' ' >> $@
$(AM_V_at)echo ' ' >> $@
$(AM_V_at)echo ' ' >> $@
$(AM_V_at)echo ' ' >> $@
$(AM_V_at)echo ' none' >> $@
$(AM_V_at)echo ' pacemaker-0.6' >> $@
$(AM_V_at)echo ' transitional-0.6' >> $@
$(AM_V_at)echo ' pacemaker-0.7' >> $@
$(AM_V_at)echo ' pacemaker-1.1' >> $@
$(AM_V_at)for rng in $(CIB_versions); do echo " pacemaker-$$rng" >> $@; done
$(AM_V_at)echo ' ' >> $@
$(AM_V_at)echo ' ' >> $@
$(AM_V_at)echo ' ' >> $@
$(AM_V_at)echo ' ' >> $@
$(AM_V_at)echo ' ' >> $@
$(AM_V_at)echo ' ' >> $@
$(AM_V_at)echo ' ' >> $@
$(AM_V_at)echo ' ' >> $@
$(AM_V_SCHEMA)echo '' >> $@
.PHONY: diff
diff: rng-helper
@echo "# Comparing changes in + since $(CIB_max)"
@$(builddir)/rng-helper diff ${CIB_version_pairs_last}
.PHONY: fulldiff
fulldiff: rng-helper
@echo "# Comparing all changes across all the subsequent increments"
@$(builddir)/rng-helper diff ${CIB_version_pairs}
CLEANFILES = $(API_generated) \
$(CIB_generated) \
$(MON_generated)
# Remove pacemaker schema files generated by *any* source version. This allows
# "make -C xml clean" to have the desired effect when checking out an earlier
# revision in a source tree.
.PHONY: clean-local
clean-local:
if [ "x$(srcdir)" != "x$(builddir)" ]; then \
rm -f $(API_build_copies) $(CIB_build_copies) $(MON_build_copies); \
fi
rm -f $(builddir)/pacemaker-[0-9]*.[0-9]*.rng
# 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/rng-helper.in b/xml/rng-helper.in
index 0bc5d91337..d3cf0578ef 100755
--- a/xml/rng-helper.in
+++ b/xml/rng-helper.in
@@ -1,209 +1,248 @@
#!@BASH_PATH@
#
# Copyright 2014-2024 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.
#
list_candidates() {
ls -1 "${1}.rng" "${1}"-[0-9]*.rng "${1}"-next.rng 2>/dev/null
}
version_from_filename() {
vff_filename="$1"
case "$vff_filename" in
*-*.rng)
echo "$vff_filename" | sed -e 's/.*-\(.*\).rng/\1/'
;;
*)
# special case for bare ${base}.rng, no -0.1's around anyway
echo 0.1
;;
esac
}
filename_from_version() {
ffv_version="$1"
ffv_base="$2"
if [ "$ffv_version" = "0.1" ]; then
echo "${ffv_base}.rng"
else
echo "${ffv_base}-${ffv_version}.rng"
fi
}
# Convert version string (e.g. 2.10) into integer (e.g. 2010) for comparisons
int_version() {
echo "$1" | awk -F. '{ printf("%d%03d\n", $1,$2); }';
}
# Find the (sub-)schema that best matches a desired version.
#
# Version numbers are assumed to be in the format X.Y,
# where X and Y are integers, and Y is no more than 3 digits,
# or the special value "next".
best_match() {
# (Sub-)schema name (e.g. "resources")
local base="$1"
# Desired version (e.g. "1.0" or "next")
local target="$2"
# If not empty, append the best match as an XML externalRef to this file
# (otherwise, just echo the best match).
local destination="$3"
# Arbitrary text to print before XML (generally spaces to indent)
local prefix="$4"
best="0.0"
for rng in $(list_candidates "${base}"); do
case ${rng} in
${base}-${target}.rng)
# We found exactly what was requested
best=${target}
break
;;
*-next.rng)
# "Next" schemas cannot be a best match unless directly requested
;;
*)
v=$(version_from_filename "${rng}")
if [ $(int_version "${v}") -gt $(int_version "${best}") ]; then
# This version beats the previous best match
if [ "${target}" = "next" ]; then
best=${v}
elif [ $(int_version "${v}") -lt $(int_version "${target}") ]; then
# This value is best only if it's still less than the target
best=${v}
fi
fi
;;
esac
done
if [ "$best" != "0.0" ]; then
found=$(filename_from_version "$best" "$base")
if [ -z "$destination" ]; then
echo "$(basename $found)"
else
echo "${prefix}" >> "$destination"
fi
return 0
fi
return 1
}
version_diff() {
# 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
for p in $*; 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 "$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 -r hline; do
if read -r h; then
read -r i
else
break
fi
iline=$(grep -Fn "$i" "$v" | cut -d: -f1)
if [ "$(echo "$iline" | wc -l)" = "1" ]; then
ctxt=$({ sed -n -e "1,$((iline - 1))p" "$v"
echo "$i"
sed -n -e "$((iline + 1)),$ p" "$v"
} | xsltproc --param skip 1 context-of.xsl -)
else
ctxt="(not detected)"
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
}
build_api_rng() {
local FILENAME="$1"
local VERSION="$2"
shift 2
cat <"$FILENAME"
EOF
for RNG in "$@"; do
- best_match "api/$RNG" "$VERSION" "$FILENAME" " " || :
+ best_match "api/$RNG" "$VERSION" "$FILENAME" " "
done
cat <>"$FILENAME"
EOF
- best_match api/status "$VERSION" "$FILENAME" " " || :
+ best_match api/status "$VERSION" "$FILENAME" " "
cat <>"$FILENAME"
EOF
}
+build_cib_rng() {
+ local FILENAME="$1"
+ local VERSION="$2"
+
+ shift 2
+ cat <"$FILENAME"
+
+
+
+
+EOF
+ best_match cib "$VERSION" "$FILENAME" " "
+ cat <>"$FILENAME"
+
+
+EOF
+ for RNG in "$@"; do
+ best_match "$RNG" "$VERSION" "$FILENAME" " "
+ done
+ cat <>"$FILENAME"
+
+
+
+
+EOF
+ best_match status "$VERSION" "$FILENAME" " "
+ cat <>"$FILENAME"
+
+
+
+
+
+EOF
+}
+
# Allow building RNGs from a different directory
cd "$(dirname $0)"
case "$1" in
match)
# Using readlink allows building from a different directory
best_match "$2" "$3" "$(readlink -f "$4")" "$5"
;;
diff)
shift
version_diff "$@"
;;
build_api_rng)
build_api_rng "$2" "$3" "${@:4}"
;;
+ build_cib_rng)
+ build_cib_rng "$2" "$3" "${@:4}"
+ ;;
+
*)
echo "Invalid command: $1"
exit 1
;;
esac