diff --git a/xml/Makefile.am b/xml/Makefile.am
index 87cf43011b..be3c29dda3 100644
--- a/xml/Makefile.am
+++ b/xml/Makefile.am
@@ -1,266 +1,289 @@
#
# 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 PCMK_SCHEMA_DIR
# for historical reasons, while the API schema is installed in a subdirectory.
APIdir = $(PCMK_SCHEMA_DIR)/api
CIBdir = $(PCMK_SCHEMA_DIR)
MONdir = $(PCMK_SCHEMA_DIR)
basexsltdir = $(PCMK_SCHEMA_DIR)/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)
version_pairs = $(join \
$(1),$(addprefix \
-,$(wordlist \
2,$(words $(1)),$(1) \
) \
) \
)
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 \
iso8601 \
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-0.xsl \
$(wildcard $(abs_srcdir)/upgrade-2.10-[0-2].xsl) \
$(wildcard $(abs_srcdir)/upgrade-3.10-*.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-0.xsl \
$(wildcard $(srcdir)/upgrade-2.10-[0-2].xsl) \
$(wildcard $(srcdir)/upgrade-3.10-*.xsl)
MON_files = $(srcdir)/crm_mon.rng
# Sorted lists of all schema versions
API_versions = $(call numeric_versions,${API_files})
CIB_versions = $(call numeric_versions,${CIB_files})
MON_versions = $(call numeric_versions,$(wildcard $(srcdir)/api/crm_mon*.rng))
# The highest numeric schema version
API_max ?= $(lastword $(API_versions))
CIB_max ?= $(lastword $(CIB_versions))
MON_max ?= $(lastword $(MON_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_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 \
upgrade-detail.xsl \
xslt_cibtr-2.rng
.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_SCHEMA)$(builddir)/rng-helper build_cib_rng "$@" "$*" \
$(CIB_cfg_base)
# Dynamically generated CIB schema listing all pacemaker versions
#
# @COMPAT none is deprecated since 2.1.8, as is validate-with being optional
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)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 '' >> $@
+schemas:
+ @if [ -z "$$SCHEMAS" ]; then \
+ ls *.rng; \
+ ls *.rng | sort -V | awk 'gsub("-[0-9.]+.rng", ""){if (last != $$0) {print; last=$$0} }'; \
+ printf "\nusage: make schemas SCHEMAS=\"\" [NEW_VERSION=\"\"]\n \
+ \nNot specifying NEW_VERSION will increase the last field of the newest version of the schema(s).\n"; \
+ else \
+ if [ -z "$$NEW_VERSION" ]; then \
+ OLD_VERSION=$$(ls *[0-9].rng | awk -F'-' 'gsub(".rng$$", "") {print $$NF}' | sort -Vu | tail -1); \
+ lf=$$(echo "$$OLD_VERSION" | awk -F"." '{print $$NF}'); \
+ ! echo "$$lf" | grep -q "^[[:digit:]]\+$$" && echo "Unable to detect version. Use NEW_VERSION= to specify version." && exit 1; \
+ lf=$$((lf+1)); \
+ NEW_VERSION=$$(echo "$$OLD_VERSION" | sed "s/[0-9]\+$$/$$lf/"); \
+ fi; \
+ for schema in $$SCHEMAS; do \
+ old_schema=$$(ls $$schema-[0-9]*.rng | sort -V | tail -1); \
+ new_schema=$$schema-$$NEW_VERSION.rng; \
+ echo "Copying $$old_schema to $$new_schema"; \
+ cp -n "$$old_schema" "$$new_schema"; \
+ done \
+ fi
+
+
.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/README.md b/xml/README.md
index 1fbd42c4e5..a53805154f 100644
--- a/xml/README.md
+++ b/xml/README.md
@@ -1,137 +1,141 @@
# Schema Reference
Pacemaker's XML schema has a version of its own, independent of the version of
Pacemaker itself.
## Versioned Schema Evolution
A versioned schema offers transparent backward and forward compatibility.
- It reflects the timeline of schema-backed features (introduction,
changes to the syntax, possibly deprecation) through the versioned
stable schema increments, while keeping schema versions used by default
by older Pacemaker versions untouched.
- Pacemaker internally uses the latest stable schema version, and relies on
supplemental transformations to promote cluster configurations based on
older, incompatible schema versions into the desired form.
## Mapping Pacemaker Versions to Schema Versions
| Pacemaker | Latest Schema | Changed
| --------- | ------------- | ----------------------------------------------
| `2.1.8` | `3.10` | `alerts`, `constraints`, `nodes`, `nvset`,
| | | `options`, `resources`, `rule`
| `2.1.5` | `3.9` | `alerts`, `constraints`, `nodes`, `nvset`,
| | | `options`, `resources`, `rule`
| `2.1.3` | `3.8` | `acls`
| `2.1.0` | `3.7` | `constraints`, `resources`
| `2.0.5` | `3.5` | `api`, `resources`, `rule`
| `2.0.4` | `3.3` | `tags`
| `2.0.1` | `3.2` | `resources`
| `2.0.0` | `3.1` | `constraints`, `resources`
| `1.1.18` | `2.10` | `resources`, `alerts`
| `1.1.17` | `2.9` | `resources`, `rule`
| `1.1.16` | `2.6` | `constraints`
| `1.1.15` | `2.5` | `alerts`
| `1.1.14` | `2.4` | `fencing`
| `1.1.13` | `2.3` | `constraints`
| `1.1.12` | `2.0` | `nodes`, `nvset`, `resources`, `tags`, `acls`
| `1.1.8` | `1.2` |
## Schema generation
Each logical portion of the schema goes into its own RNG file, named like
`${base}-${X}.${Y}.rng`. `${base}` identifies the portion of the schema
(e.g. constraints, resources); ${X}.${Y} is the latest schema version that
contained changes in this portion of the schema.
The complete, overall schema, `pacemaker-${X}.${Y}.rng`, is automatically
generated from the other files via the Makefile.
# Updating schema files #
## New features ##
The current schema version is determined at runtime when
crm\_schema\_init() scans the CRM\_SCHEMA\_DIRECTORY.
It will have the form `pacemaker-${X}.${Y}` and the highest
`${X}.${Y}` wins.
### Simple Additions
When the new syntax is a simple addition to the previous one, create a
new entry, incrementing `${Y}`.
### Feature Removal or otherwise Incompatible Changes
When the new syntax is not a simple addition to the previous one,
create a new entry, incrementing `${X}` and setting `${Y} = 0`.
An XSLT file is also required that converts an old syntax to the new
one and must be named `upgrade-${Xold}.${Yold}.xsl`.
See `xml/upgrade-1.3.xsl` for an example.
Since `xml/upgrade-2.10.xsl`, rather self-descriptive approach is taken,
separating metadata of the replacements and other modifications to
perform from the actual executive parts, which is leveraged, e.g., with
the on-the-fly overview as obtained with `./regression.sh -X test2to3`.
Also this was the first time particular key names of `nvpair`s,
i.e. below the granularity of the schemas so far, received attention,
and consequently, no longer expected names became systemically banned
in the after-upgrade schemas, using `` construct in the
data type specification pertaining the affected XML path.
The implied complexity also resulted in establishing a new compound,
stepwise transformation, alleviating the procedural burden from the
core upgrade recipe. In particular, `id-ref` based syntactic
simplification granted in the CIB format introduces nonnegligible
internal "noise" because of the extra indirection encumbered with
generally non-bijective character of such a scheme (context-dependent
interpretation). To reduce this strain, a symmetric arrangement is
introduced as a pair of _enter_/_leave_ (pre-upgrade/post-upgrade)
transformations where the latter is meant to eventually reversibly
restore what the former intentionally simplified (normalized) for
upgrade transformation's peruse. It's optional (even the post-upgrade
counterpart is optional alone) and depends on whether the suitable
files are found along the upgrade transformation itself: e.g., for
`upgrade-2.10.xsl`, such files are `upgrade-2.10-enter.xsl` and
`upgrade-2.10-leave.xsl`. Note that unfolding + refolding `id-ref`
shortcuts is just a practically imposed individual case of how to
reversibly make the configuration space tractable in the upgrade
itself, allowing for more sophistication down the road.
### General Procedure
-1. Copy the most recent version of `${base}-*.rng` to `${base}-${X}.${Y}.rng`,
- such that the new file name increments the highest number of any schema file,
- not just the file being edited.
+1. Run `make -C xml schemas SCHEMAS="${base}" NEW_VERSION="${X}.${Y}"` to copy
+ the most recent version of `${base}` to a higher version than the highest
+ version of any schema file.
+ Multiple `SCHEMAS` can be entered (space-separated), and `${Y}` will be
+ automatically increased if `NEW_VERSION` is omitted.
+ Run `make -C xml schemas` to list all versions of the schemas, schema-list
+ for easy copy/paste, and usage info.
2. Commit the copy, e.g. `"Low: xml: clone ${base} schema in preparation for
changes"`. This way, the actual change will be obvious in the commit history.
3. Modify `${base}-${X}.${Y}.rng` as required.
4. If required, add an XSLT file, and update `xslt\_SCRIPTS` in `xml/Makefile.am`.
5. Commit.
6. Run `make -C xml clean; make -C xml` to rebuild the schemas in the local
source directory.
7. The CIB validity and upgrade regression tests will break after the schema is
updated. Run `cts/cts-cli -s` to make the expected outputs reflect the
changes made so far, and run `git diff` to ensure that these changes look
sane. Finally, commit the changes.
8. Similarly, with the new major version `${X}`, it's advisable to refresh
scheduler tests at some point. See the instructions in `cts/README.md`.
## Using a New Schema
New features will not be available until the cluster administrator:
1. Updates all the nodes
2. Runs the equivalent of `cibadmin --upgrade --force`
## Random Notes
From the source directory, run `make -C xml diff` to see the changes
in the current schema (compared to the previous ones).
Alternatively, if the intention is to grok the overall historical schema
evolution, use `make -C xml fulldiff`.