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`.