diff --git a/cts/cli/regression.dates.exp b/cts/cli/regression.dates.exp
index 9630eff7377..e5f14314cd2 100644
--- a/cts/cli/regression.dates.exp
+++ b/cts/cli/regression.dates.exp
@@ -78,6 +78,20 @@ crm_time_parse_duration error: 'P1YM/2019-02-20 00:00:00Z' is not a valid ISO 8
iso8601: Invalid interval specified: P1YM/2019-02-20 00:00:00Z
=#=#=#= End test: Invalid period - [P1YM/2019-02-20 00:00:00Z] - Invalid parameter (2) =#=#=#=
* Passed: iso8601 - Invalid period - [P1YM/2019-02-20 00:00:00Z]
+=#=#=#= Begin test: '2005-040/2005-043' period =#=#=#=
+Period: 2005-02-09 00:00:00Z to 2005-02-12 00:00:00Z
+=#=#=#= End test: '2005-040/2005-043' period - OK (0) =#=#=#=
+* Passed: iso8601 - '2005-040/2005-043' period
+=#=#=#= Begin test: '2005-040/2005-043' period (XML) =#=#=#=
+
+
+ 2005-02-09 00:00:00Z
+ 2005-02-12 00:00:00Z
+
+
+
+=#=#=#= End test: '2005-040/2005-043' period (XML) - OK (0) =#=#=#=
+* Passed: iso8601 - '2005-040/2005-043' period (XML)
=#=#=#= Begin test: 2014-01-01 00:30:00 - 1 Hour =#=#=#=
Date: 2014-01-01 00:30:00Z
Duration: -3600 seconds (1 hour)
@@ -324,6 +338,13 @@ Date: 2040-W01-1 00:00:00Z
Date: 2009-W53-7 00:00:00Z
=#=#=#= End test: 2009-W53-07 - OK (0) =#=#=#=
* Passed: iso8601 - 2009-W53-07
+=#=#=#= Begin test: 2009-W53-07 (XML) =#=#=#=
+
+ 2009-W53-7 00:00:00Z
+
+
+=#=#=#= End test: 2009-W53-07 (XML) - OK (0) =#=#=#=
+* Passed: iso8601 - 2009-W53-07 (XML)
=#=#=#= Begin test: epoch + 2 Years 5 Months 6 Minutes =#=#=#=
Date: 1970-01-01 00:00:00Z
Duration: 2 years 5 months 360 seconds (6 minutes)
@@ -354,9 +375,27 @@ Duration: -1 months
Duration ends at: 2009-02-28 01:00:00 +01:00
=#=#=#= End test: 2009-03-31 - 1 Month - OK (0) =#=#=#=
* Passed: iso8601 - 2009-03-31 - 1 Month
+=#=#=#= Begin test: 2009-03-31 - 1 Month (XML) =#=#=#=
+
+ 2009-03-31 00:00:00Z
+ -1 months
+ 2009-02-28 01:00:00 +01:00
+
+
+=#=#=#= End test: 2009-03-31 - 1 Month (XML) - OK (0) =#=#=#=
+* Passed: iso8601 - 2009-03-31 - 1 Month (XML)
=#=#=#= Begin test: 2038-01-01 + 3 Months =#=#=#=
Date: 2038-01-01 00:00:00Z
Duration: 3 months
Duration ends at: 2038-04-01 00:00:00Z
=#=#=#= End test: 2038-01-01 + 3 Months - OK (0) =#=#=#=
* Passed: iso8601 - 2038-01-01 + 3 Months
+=#=#=#= Begin test: 2038-01-01 + 3 Months (XML) =#=#=#=
+
+ 2038-01-01 00:00:00Z
+ 3 months
+ 2038-04-01 00:00:00Z
+
+
+=#=#=#= End test: 2038-01-01 + 3 Months (XML) - OK (0) =#=#=#=
+* Passed: iso8601 - 2038-01-01 + 3 Months (XML)
diff --git a/cts/cts-cli.in b/cts/cts-cli.in
index 9cc09a4210e..378e61e3782 100755
--- a/cts/cts-cli.in
+++ b/cts/cts-cli.in
@@ -2561,6 +2561,14 @@ function test_dates() {
test_assert $CRM_EX_INVALID_PARAM 0
done
+ desc="'2005-040/2005-043' period"
+ cmd="iso8601 -p '2005-040/2005-043'"
+ test_assert $CRM_EX_OK 0
+
+ desc="'2005-040/2005-043' period (XML)"
+ cmd="iso8601 -p '2005-040/2005-043' --output-as=xml"
+ test_assert_validate $CRM_EX_OK 0
+
desc="2014-01-01 00:30:00 - 1 Hour"
cmd="iso8601 -d '2014-01-01 00:30:00Z' -D P-1H -E '2013-12-31 23:30:00Z'"
test_assert $CRM_EX_OK 0
@@ -2599,6 +2607,10 @@ function test_dates() {
cmd="iso8601 -d '2009-W53-7 00:00:00Z' -W -E '2009-W53-7 00:00:00Z'"
test_assert $CRM_EX_OK 0
+ desc="2009-W53-07 (XML)"
+ cmd="iso8601 -d '2009-W53-7 00:00:00Z' -W -E '2009-W53-7 00:00:00Z' --output-as=xml"
+ test_assert_validate $CRM_EX_OK 0
+
desc="epoch + 2 Years 5 Months 6 Minutes"
cmd="iso8601 -d 'epoch' -D P2Y5MT6M -E '1972-06-01 00:06:00Z'"
test_assert $CRM_EX_OK 0
@@ -2619,9 +2631,17 @@ function test_dates() {
cmd="iso8601 -d '2009-03-31 01:00:00 +01:00' -D P-1M -E '2009-02-28 00:00:00Z'"
test_assert $CRM_EX_OK 0
+ desc="2009-03-31 - 1 Month (XML)"
+ cmd="iso8601 -d '2009-03-31 01:00:00 +01:00' -D P-1M -E '2009-02-28 00:00:00Z' --output-as=xml"
+ test_assert_validate $CRM_EX_OK 0
+
desc="2038-01-01 + 3 Months"
cmd="iso8601 -d '2038-01-01 00:00:00Z' -D P3M -E '2038-04-01 00:00:00Z'"
test_assert $CRM_EX_OK 0
+
+ desc="2038-01-01 + 3 Months (XML)"
+ cmd="iso8601 -d '2038-01-01 00:00:00Z' -D P3M -E '2038-04-01 00:00:00Z' --output-as=xml"
+ test_assert_validate $CRM_EX_OK 0
}
function test_acl_loop() {
@@ -3559,6 +3579,7 @@ for t in $tests; do
-e 's/ version="[^"]*"/ version=""/' \
-e 's/.*Relax-NG validity error : //' \
-e 's/request=\".*\(crm_[a-zA-Z0-9]*\)/request=\"\1/' \
+ -e 's/request=\".*iso8601/request=\"iso8601/' \
-e 's/crm_feature_set="[^"]*" //'\
-e 's/@crm_feature_set=[0-9.]*, //'\
-e 's/\( $@
@@ -103,17 +80,17 @@ global-clean:
.PHONY: manhtml
manhtml:
$(MAKE) $(AM_MAKEFLAGS) -C .. all
- find ../agents ../daemons ../tools -name "[a-z]*.[78]" \
+ find $(MANPAGE_DIRS) -name "[a-z]*.[78]" \
-exec $(MAKE) $(AM_MAKEFLAGS) \{\}.html \;
.PHONY: manhtml-upload
manhtml-upload: manhtml
- find .. -name "[a-z]*.[78].html" -exec \
- rsync $(RSYNC_OPTS) \{\} "$(RSYNC_DEST)/$(PACKAGE)/man/" \;
+ find $(MANPAGE_DIRS) -name "[a-z]*.[78].html" -exec \
+ rsync $(RSYNC_OPTS) \{\} "$(RSYNC_PACKAGE_DEST)/man/" \;
.PHONY: manhtml-clean
manhtml-clean:
- -find .. -name "[a-z]*.[78].html" -exec rm \{\} \;
+ -find $(MANPAGE_DIRS) -name "[a-z]*.[78].html" -exec rm \{\} \;
# API documentation as HTML
@@ -124,7 +101,7 @@ doxygen: Doxyfile
.PHONY: doxygen-upload
doxygen-upload: doxygen
- rsync $(RSYNC_OPTS) api/html/ "$(RSYNC_DEST)/$(PACKAGE)/doxygen/$(TAG)/"
+ rsync $(RSYNC_OPTS) api/html/ "$(RSYNC_PACKAGE_DEST)/doxygen/$(TAG)/"
.PHONY: doxygen-clean
doxygen-clean:
@@ -139,7 +116,8 @@ abi: abi-check
.PHONY: abi-www
abi-www:
- export RSYNC_DEST=$(RSYNC_DEST); ./abi-check -u $(PACKAGE) $(LAST_RELEASE) $(TAG)
+ export RSYNC_PACKAGE_DEST=$(RSYNC_PACKAGE_DEST); \
+ ./abi-check -u $(PACKAGE) $(LAST_RELEASE) $(TAG)
.PHONY: abi-clean
abi-clean:
@@ -158,10 +136,10 @@ books-upload:
# All online documentation (except ABI compatibility, which is run separately)
.PHONY: www
-www: clean-local deprecated-upload manhtml-upload global-upload doxygen-upload books-upload
+www: clean-local manhtml-upload global-upload doxygen-upload books-upload
.PHONY: clean-local
-clean-local: global-clean manhtml-clean doxygen-clean abi-clean deprecated-clean
+clean-local: global-clean manhtml-clean doxygen-clean abi-clean
# "make check" will cause "make all" to be run, which means docs will get built
# as a part of running tests if they haven't already. That seems unnecessary, so
diff --git a/doc/abi-check.in b/doc/abi-check.in
index 6b6a8d3d789..754e4d39c19 100755
--- a/doc/abi-check.in
+++ b/doc/abi-check.in
@@ -1,6 +1,6 @@
#!@BASH_PATH@
#
-# Copyright 2011-2023 the Pacemaker project contributors
+# Copyright 2011-2024 the Pacemaker project contributors
#
# The version control history for this file may have further details.
#
@@ -16,8 +16,8 @@
# upload it to the website.
#
-# Top-level rsync destination for www targets (without trailing slash)
-: ${RSYNC_DEST:=root@www.clusterlabs.org:/var/www/html}
+# Top-level rsync destination for package's file uploads (without trailing slash)
+: ${RSYNC_PACKAGE_DEST:=sites.clusterlabs.org:/var/www/html/projects/$(PACKAGE)}
# If the argument is of form x.y.z, print Pacemaker-x.y.z,
# otherwise print the argument (presumably a commit ID) directly
@@ -159,6 +159,6 @@ if [ $# -eq 2 ]; then
COMPAT_REPORT="compat_reports/${PACKAGE}/${V1}_to_${V2}"
if [ $UPLOAD -eq 1 ] && [ -d "$COMPAT_REPORT" ]; then
- rsync -azxlSD --progress "$COMPAT_REPORT" "${RSYNC_DEST}/${PACKAGE}/abi/"
+ rsync -azxlSD --progress "$COMPAT_REPORT" "${RSYNC_PACKAGE_DEST}/abi/"
fi
fi
diff --git a/doc/clusterlabs-logo-55x55.png b/doc/clusterlabs-logo-55x55.png
new file mode 100644
index 00000000000..cab0249e52d
Binary files /dev/null and b/doc/clusterlabs-logo-55x55.png differ
diff --git a/doc/crm_fencing.txt b/doc/crm_fencing.txt
deleted file mode 100644
index 26acde76710..00000000000
--- a/doc/crm_fencing.txt
+++ /dev/null
@@ -1,439 +0,0 @@
-Fencing and Stonith
-===================
-Dejan_Muhamedagic
-v0.9
-
-Fencing is a very important concept in computer clusters for HA
-(High Availability). Unfortunately, given that fencing does not
-offer a visible service to users, it is often neglected.
-
-Fencing may be defined as a method to bring an HA cluster to a
-known state. But, what is a "cluster state" after all? To answer
-that question we have to see what is in the cluster.
-
-== Introduction to HA clusters
-
-Any computer cluster may be loosely defined as a collection of
-cooperating computers or nodes. Nodes talk to each other over
-communication channels, which are typically standard network
-connections, such as Ethernet.
-
-The main purpose of an HA cluster is to manage user services.
-Typical examples of user services are an Apache web server or,
-say, a MySQL database. From the user's point of view, the
-services do some specific and hopefully useful work when ordered
-to do so. To the cluster, however, they are just things which may
-be started or stopped. This distinction is important, because the
-nature of the service is irrelevant to the cluster. In the
-cluster lingo, the user services are known as resources.
-
-Every resource has a state attached, for instance: "resource r1
-is started on node1". In an HA cluster, such state implies that
-"resource r1 is stopped on all nodes but node1", because an HA
-cluster must make sure that every resource may be started on at
-most one node.
-
-A collection of resource states and node states is the cluster
-state.
-
-Every node must report every change that happens to resources.
-This may happen only for the running resources, because a node
-should not start resources unless told so by somebody. That
-somebody is the Cluster Resource Manager (CRM) in our case.
-
-So far so good. But what if, for whatever reason, we cannot
-establish with certainty a state of some node or resource? This
-is where fencing comes in. With fencing, even when the cluster
-doesn't know what is happening on some node, we can make sure
-that that node doesn't run any or certain important resources.
-
-If you wonder how this can happen, there may be many risks
-involved with computing: reckless people, power outages, natural
-disasters, rodents, thieves, software bugs, just to name a few.
-We are sure that at least a few times your computer failed
-unpredictably.
-
-== Fencing
-
-There are two kinds of fencing: resource level and node level.
-
-Using the resource level fencing the cluster can make sure that
-a node cannot access one or more resources. One typical example
-is a SAN, where a fencing operation changes rules on a SAN switch
-to deny access from a node.
-
-The resource level fencing may be achieved using normal resources
-on which the resource we want to protect would depend. Such a
-resource would simply refuse to start on this node and therefore
-resources which depend on it will be unrunnable on the same node
-as well.
-
-The node level fencing makes sure that a node does not run any
-resources at all. This is usually done in a very simple, yet
-brutal way: the node is simply reset using a power switch. This
-may ultimately be necessary because the node may not be
-responsive at all.
-
-The node level fencing is our primary subject below.
-
-== Node level fencing devices
-
-Before we get into the configuration details, you need to pick a
-fencing device for the node level fencing. There are quite a few
-to choose from. If you want to see the list of stonith devices
-which are supported just run:
-
- stonith -L
-
-Stonith devices may be classified into five categories:
-
-- UPS (Uninterruptible Power Supply)
-
-- PDU (Power Distribution Unit)
-
-- Blade power control devices
-
-- Lights-out devices
-
-- Testing devices
-
-The choice depends mainly on your budget and the kind of
-hardware. For instance, if you're running a cluster on a set of
-blades, then the power control device in the blade enclosure is
-the only candidate for fencing. Of course, this device must be
-capable of managing single blade computers.
-
-The lights-out devices (IBM RSA, HP iLO, Dell DRAC) are becoming
-increasingly popular and in future they may even become standard
-equipment of of-the-shelf computers. They are, however, inferior
-to UPS devices, because they share a power supply with their host
-(a cluster node). If a node stays without power, the device
-supposed to control it would be just as useless. Even though this
-is obvious to us, the cluster manager is not in the know and will
-try to fence the node in vain. This will continue forever because
-all other resource operations would wait for the fencing/stonith
-operation to succeed.
-
-The testing devices are used exclusively for testing purposes.
-They are usually more gentle on the hardware. Once the cluster
-goes into production, they must be replaced with real fencing
-devices.
-
-== STONITH (Shoot The Other Node In The Head)
-
-Stonith is our fencing implementation. It provides the node level
-fencing.
-
-.NB
-The stonith and fencing terms are often used
-interchangeably here as well as in other texts.
-
-The stonith subsystem consists of two components:
-
-- pacemaker-fenced
-
-- stonith plugins
-
-=== pacemaker-fenced
-
-pacemaker-fenced is a daemon which may be accessed by the local processes
-or over the network. It accepts commands which correspond to
-fencing operations: reset, power-off, and power-on. It may also
-check the status of the fencing device.
-
-pacemaker-fenced runs on every node in the CRM HA cluster. The
-pacemaker-fenced instance running on the DC node receives a fencing
-request from the CRM. It is up to this and other pacemaker-fenced
-programs to carry out the desired fencing operation.
-
-=== Stonith plugins
-
-For every supported fencing device there is a stonith plugin
-which is capable of controlling that device. A stonith plugin is
-the interface to the fencing device. All stonith plugins look the
-same to pacemaker-fenced, but are quite different on the other side
-reflecting the nature of the fencing device.
-
-Some plugins support more than one device. A typical example is
-ipmilan (or external/ipmi) which implements the IPMI protocol and
-can control any device which supports this protocol.
-
-== CRM stonith configuration
-
-The fencing configuration consists of one or more stonith
-resources.
-
-A stonith resource is a resource of class stonith and it is
-configured just like any other resource. The list of parameters
-(attributes) depend on and are specific to a stonith type. Use
-the stonith(1) program to see the list:
-
- $ stonith -t ibmhmc -n
- ipaddr
- $ stonith -t ipmilan -n
- hostname ipaddr port auth priv login password reset_method
-
-.NB
-It is easy to guess the class of a fencing device from
-the set of attribute names.
-
-A short help text is also available:
-
- $ stonith -t ibmhmc -h
- STONITH Device: ibmhmc - IBM Hardware Management Console (HMC)
- Use for IBM i5, p5, pSeries and OpenPower systems managed by HMC
- Optional parameter name managedsyspat is white-space delimited
- list of patterns used to match managed system names; if last
- character is '*', all names that begin with the pattern are matched
- Optional parameter name password is password for hscroot if
- passwordless ssh access to HMC has NOT been setup (to do so,
- it is necessary to create a public/private key pair with
- empty passphrase - see "Configure the OpenSSH client" in the
- redbook for more details)
- For more information see
- http://publib-b.boulder.ibm.com/redbooks.nsf/RedbookAbstracts/SG247038.html
-
-.You just said that there is pacemaker-fenced and stonith plugins. What's with these resources now?
-**************************
-Resources of class stonith are just a representation of stonith
-plugins in the CIB. Well, a bit more: apart from the fencing
-operations, the stonith resources, just like any other, may be
-started and stopped and monitored. The start and stop operations
-are a bit of a misnomer: enable and disable would serve better,
-but it's too late to change that. So, these two are actually
-administrative operations and do not translate to any operation
-on the fencing device itself. Monitor, however, does translate to
-device status.
-**************************
-
-A dummy stonith resource configuration, which may be used in some
-testing scenarios is very simple:
-
- configure
- primitive st-null stonith:null \
- params hostlist="node1 node2"
- clone fencing st-null
- commit
-
-.NB
-**************************
-All configuration examples are in the crm configuration tool
-syntax. To apply them, put the sample in a text file, say
-sample.txt and run:
-
- crm < sample.txt
-
-The configure and commit lines are omitted from further examples.
-**************************
-
-An alternative configuration:
-
- primitive st-node1 stonith:null \
- params hostlist="node1"
- primitive st-node2 stonith:null \
- params hostlist="node2"
- location l-st-node1 st-node1 -inf: node1
- location l-st-node2 st-node2 -inf: node2
-
-This configuration is perfectly alright as far as the cluster
-software is concerned. The only difference to a real world
-configuration is that no fencing operation takes place.
-
-A more realistic, but still only for testing, is the following
-external/ssh configuration:
-
- primitive st-ssh stonith:external/ssh \
- params hostlist="node1 node2"
- clone fencing st-ssh
-
-This one can also reset nodes. As you can see, this configuration
-is remarkably similar to the first one which features the null
-stonith device.
-
-.What is this clone thing?
-**************************
-Clones are a CRM/Pacemaker feature. A clone is basically a
-shortcut: instead of defining _n_ identical, yet differently named
-resources, a single cloned resource suffices. By far the most
-common use of clones is with stonith resources if the stonith
-device is accessible from all nodes.
-**************************
-
-The real device configuration is not much different, though some
-devices may require more attributes. For instance, an IBM RSA
-lights-out device might be configured like this:
-
- primitive st-ibmrsa-1 stonith:external/ibmrsa-telnet \
- params nodename=node1 ipaddr=192.168.0.101 \
- userid=USERID passwd=PASSW0RD
- primitive st-ibmrsa-2 stonith:external/ibmrsa-telnet \
- params nodename=node2 ipaddr=192.168.0.102 \
- userid=USERID passwd=PASSW0RD
- # st-ibmrsa-1 can run anywhere but on node1
- location l-st-node1 st-ibmrsa-1 -inf: node1
- # st-ibmrsa-2 can run anywhere but on node2
- location l-st-node2 st-ibmrsa-2 -inf: node2
-
-.Why those strange location constraints?
-**************************
-There is always certain probability that the stonith operation is
-going to fail. Hence, a stonith operation on the node which is
-the executioner too is not reliable. If the node is reset, then
-it cannot send the notification about the fencing operation
-outcome.
-**************************
-
-If you haven't already guessed, configuration of a UPS kind of
-fencing device is remarkably similar to all we have already
-shown.
-
-All UPS devices employ the same mechanics for fencing. What is,
-however, different is how the device itself is accessed. Old UPS
-devices, those that were considered professional, used to have
-just a serial port, typically connected at 1200baud using a
-special serial cable. Many new ones still come equipped with a
-serial port, but often they also sport a USB interface or an
-Ethernet interface. The kind of connection we may make use of
-depends on what the plugin supports. Let's see a few examples for
-the APC UPS equipment:
-
- $ stonith -t apcmaster -h
-
- STONITH Device: apcmaster - APC MasterSwitch (via telnet)
- NOTE: The APC MasterSwitch accepts only one (telnet)
- connection/session a time. When one session is active,
- subsequent attempts to connect to the MasterSwitch will fail.
- For more information see http://www.apc.com/
- List of valid parameter names for apcmaster STONITH device:
- ipaddr
- login
- password
-
- $ stonith -t apcsmart -h
-
- STONITH Device: apcsmart - APC Smart UPS
- (via serial port - NOT USB!).
- Works with higher-end APC UPSes, like
- Back-UPS Pro, Smart-UPS, Matrix-UPS, etc.
- (Smart-UPS may have to be >= Smart-UPS 700?).
- See http://www.networkupstools.org/protocols/apcsmart.html
- for protocol compatibility details.
- For more information see http://www.apc.com/
- List of valid parameter names for apcsmart STONITH device:
- ttydev
- hostlist
-
-The former plugin supports APC UPS with a network port and telnet
-protocol. The latter plugin uses the APC SMART protocol over the
-serial line which is supported by many different APC UPS product
-lines.
-
-.So, what do I use: clones, constraints, both?
-**************************
-It depends. Depends on the nature of the fencing device. For
-example, if the device cannot serve more than one connection at
-the time, then clones won't do. Depends on how many hosts can the
-device manage. If it's only one, and that is always the case with
-lights-out devices, then again clones are right out. Depends
-also on the number of nodes in your cluster: the more nodes the
-more desirable to use clones. Finally, it is also a matter of
-personal preference.
-
-In short: if clones are safe to use with your configuration and
-if they reduce the configuration, then make cloned stonith
-resources.
-**************************
-
-The CRM configuration is left as an exercise to the reader.
-
-== Monitoring the fencing devices
-
-Just like any other resource, the stonith class agents also
-support the monitor operation. Given that we have often seen
-monitor either not configured or configured in a wrong way, we
-have decided to devote a section to the matter.
-
-Monitoring stonith resources, which is actually checking status
-of the corresponding fencing devices, is strongly recommended. So
-strongly, that we should consider a configuration without it
-invalid.
-
-On the one hand, though an indispensable part of an HA cluster, a
-fencing device, being the last line of defense, is used seldom.
-Very seldom and preferably never. On the other, for whatever
-reason, the power management equipment is known to be rather
-fragile on the communication side. Some devices were known to
-give up if there was too much broadcast traffic on the wire. Some
-cannot handle more than ten or so connections per minute. Some
-get confused or depressed if two clients try to connect at the
-same time. Most cannot handle more than one session at the time.
-The bottom line: try not to exercise your fencing device too
-often. It may not like it. Use monitoring regularly, yet
-sparingly, say once every couple of hours. The probability that
-within those few hours there will be a need for a fencing
-operation and that the power switch would fail is usually low.
-
-== Odd plugins
-
-Apart from plugins which handle real devices, some stonith
-plugins are a bit out of line and deserve special attention.
-
-=== external/kdumpcheck
-
-Sometimes, it may be important to get a kernel core dump. This
-plugin may be used to check if the dump is in progress. If
-that is the case, then it will return true, as if the node has
-been fenced, which is actually true given that it cannot run
-any resources at the time. kdumpcheck is typically used in
-concert with another, real, fencing device. See
-README_kdumpcheck.txt for more details.
-
-=== external/sbd
-
-This is a self-fencing device. It reacts to a so-called "poison
-pill" which may be inserted into a shared disk. On shared storage
-connection loss, it also makes the node commit suicide. See
-http://www.linux-ha.org/wiki/SBD_Fencing for more details.
-
-=== meatware
-
-Strange name and a simple concept. `meatware` requires help from a
-human to operate. Whenever invoked, `meatware` logs a CRIT severity
-message which should show up on the node's console. The operator
-should then make sure that the node is down and issue a
-`meatclient(8)` command to tell `meatware` that it's OK to tell the
-cluster that it may consider the node dead. See `README.meatware`
-for more information.
-
-=== null
-
-This one is probably not of much importance to the general
-public. It is used in various testing scenarios. `null` is an
-imaginary device which always behaves and always claims that it
-has shot a node, but never does anything. Sort of a
-happy-go-lucky. Do not use it unless you know what you are doing.
-
-=== suicide
-
-`suicide` is a software-only device, which can reboot a node it is
-running on. It depends on the operating system, so it should be
-avoided whenever possible. But it is OK on one-node clusters.
-`suicide` and `null` are the only exceptions to the "don't shoot my
-host" rule.
-
-.What about that pacemaker-fenced? You forgot about it, eh?
-**************************
-The pacemaker-fenced daemon, though it is really the master of ceremony,
-requires no configuration itself. All configuration is stored in
-the CIB.
-**************************
-
-== Resources
-
-http://www.linux-ha.org/wiki/STONITH
-
-https://www.clusterlabs.org/doc/crm_fencing.html
-
-https://clusterlabs.org/pacemaker/doc/2.1/Pacemaker_Explained/html/
-
-http://techthoughts.typepad.com/managing_computers/2007/10/split-brain-quo.html
diff --git a/doc/sphinx/Makefile.am b/doc/sphinx/Makefile.am
index b95f47b9590..2ba43ea9a79 100644
--- a/doc/sphinx/Makefile.am
+++ b/doc/sphinx/Makefile.am
@@ -8,8 +8,9 @@
#
include $(top_srcdir)/mk/common.mk
-# Define release-related variables
+# Define release- and upload-related variables
include $(top_srcdir)/mk/release.mk
+include $(top_srcdir)/mk/uploads.mk
# Things you might want to override on the command line
@@ -40,9 +41,6 @@ PAPER ?= letterpaper
# Additional options for sphinx-build
SPHINXFLAGS ?=
-# toplevel rsync destination for www targets (without trailing slash)
-RSYNC_DEST ?= root@www.clusterlabs.org:/var/www/html
-
# End of useful overrides
@@ -80,13 +78,9 @@ EXTRA_DIST = $(wildcard */*.rst) $(DOTS) $(SVGS) \
$(STATIC_FILES) \
conf.py.in
-# recursive, preserve symlinks/permissions/times, verbose, compress,
-# don't cross filesystems, sparse, show progress
-RSYNC_OPTS = -rlptvzxS --progress
-
PACKAGE_SERIES=$(shell echo "$(VERSION)" | awk -F. '{ print $$1"."$$2 }')
-BOOK_RSYNC_DEST = $(RSYNC_DEST)/$(PACKAGE)/doc/$(PACKAGE_SERIES)
+BOOK_RSYNC_DEST = $(RSYNC_PACKAGE_DEST)/doc/$(PACKAGE_SERIES)
BOOK = none
@@ -175,7 +169,7 @@ if BUILD_SPHINX_DOCS
"$(BOOK_RSYNC_DEST)/$$book/"; \
done
@rsync $(RSYNC_OPTS) "$(builddir)/build-$(PACKAGE_SERIES).txt" \
- "$(RSYNC_DEST)/$(PACKAGE)/doc"
+ "$(RSYNC_PACKAGE_DEST)/doc"
endif
.PHONY: vars
@@ -184,6 +178,7 @@ vars:
@echo "PAPER='$(PAPER)'"
@echo "SPHINXFLAGS='$(SPHINXFLAGS)'"
@echo "RSYNC_DEST='$(RSYNC_DEST)'"
+ @echo "RSYNC_PACKAGE_DEST='$(RSYNC_PACKAGE_DEST)'"
@echo "VERSION='$(VERSION)'"
@echo "PACKAGE_SERIES='$(PACKAGE_SERIES)'"
diff --git a/include/crm/common/xml_names.h b/include/crm/common/xml_names.h
index 1de7fb93353..6dfd23af8b5 100644
--- a/include/crm/common/xml_names.h
+++ b/include/crm/common/xml_names.h
@@ -92,6 +92,7 @@ extern "C" {
#define PCMK_XE_CRM_MON "crm_mon"
#define PCMK_XE_CRM_MON_DISCONNECTED "crm-mon-disconnected"
#define PCMK_XE_CURRENT_DC "current_dc"
+#define PCMK_XE_DATE "date"
#define PCMK_XE_DATE_EXPRESSION "date_expression"
#define PCMK_XE_DATE_SPEC "date_spec"
#define PCMK_XE_DC "dc"
@@ -101,6 +102,8 @@ extern "C" {
#define PCMK_XE_DIGESTS "digests"
#define PCMK_XE_DOCKER "docker"
#define PCMK_XE_DURATION "duration"
+#define PCMK_XE_DURATION_ENDS "duration_ends"
+#define PCMK_XE_END "end"
#define PCMK_XE_ERROR "error"
#define PCMK_XE_ERRORS "errors"
#define PCMK_XE_EXPRESSION "expression"
@@ -154,6 +157,7 @@ extern "C" {
#define PCMK_XE_PACEMAKERD "pacemakerd"
#define PCMK_XE_PARAMETER "parameter"
#define PCMK_XE_PARAMETERS "parameters"
+#define PCMK_XE_PERIOD "period"
#define PCMK_XE_PODMAN "podman"
#define PCMK_XE_PORT_MAPPING "port-mapping"
#define PCMK_XE_POSITION "position"
@@ -196,6 +200,7 @@ extern "C" {
#define PCMK_XE_SOURCE "source"
#define PCMK_XE_SPECIAL "special"
#define PCMK_XE_STACK "stack"
+#define PCMK_XE_START "start"
#define PCMK_XE_STATUS "status"
#define PCMK_XE_STORAGE "storage"
#define PCMK_XE_STORAGE_MAPPING "storage-mapping"
diff --git a/include/crm/crm.h b/include/crm/crm.h
index 2ef84d3fd86..e862bbe5eb0 100644
--- a/include/crm/crm.h
+++ b/include/crm/crm.h
@@ -69,7 +69,7 @@ extern "C" {
* >=3.2.0: DC supports PCMK_EXEC_INVALID and PCMK_EXEC_NOT_CONNECTED
* >=3.19.0: DC supports PCMK__CIB_REQUEST_COMMIT_TRANSACT
*/
-# define CRM_FEATURE_SET "3.19.5"
+# define CRM_FEATURE_SET "3.19.6"
/* Pacemaker's CPG protocols use fixed-width binary fields for the sender and
* recipient of a CPG message. This imposes an arbitrary limit on cluster node
diff --git a/m4/version.m4 b/m4/version.m4
index 96277dfcd20..f49a9f0d478 100644
--- a/m4/version.m4
+++ b/m4/version.m4
@@ -1,2 +1,2 @@
m4_define([VERSION_NUMBER], [2.1.9])
-m4_define([PCMK_URL], [https://ClusterLabs.org/pacemaker/])
+m4_define([PCMK_URL], [https://ClusterLabs.org/projects/pacemaker/])
diff --git a/mk/uploads.mk b/mk/uploads.mk
new file mode 100644
index 00000000000..07808aec4fc
--- /dev/null
+++ b/mk/uploads.mk
@@ -0,0 +1,19 @@
+#
+# Copyright 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.
+#
+
+# Variables useful for uploading files to a server
+
+# toplevel rsync destination (without trailing slash)
+RSYNC_DEST ?= sites.clusterlabs.org:/var/www/html
+
+RSYNC_PACKAGE_DEST = $(RSYNC_DEST)/projects/$(PACKAGE)
+
+# recursive, preserve symlinks, preserve permissions, verbose, compress,
+# don't cross filesystems, sparse, show progress
+RSYNC_OPTS = -rlpvzxS --progress
diff --git a/python/pacemaker/_cts/watcher.py b/python/pacemaker/_cts/watcher.py
index 6227e31dd99..ff5ba70fba9 100644
--- a/python/pacemaker/_cts/watcher.py
+++ b/python/pacemaker/_cts/watcher.py
@@ -9,13 +9,43 @@
import time
import threading
-from dateutil.parser import isoparser
-
from pacemaker.buildoptions import BuildOptions
from pacemaker._cts.errors import OutputNotFoundError
from pacemaker._cts.logging import LogFactory
from pacemaker._cts.remote import RemoteFactory
+# dateutil >=2.7.0 provides isoparser, but we support older versions
+try:
+ from dateutil.parser import isoparser
+except ImportError:
+ import datetime
+ import subprocess
+ from pacemaker._cts.process import pipe_communicate
+
+ class isoparser():
+ """Mimic isoparser.isoparse() when not available"""
+
+ def isoparse(self, timestamp):
+ """Mimic isoparser.isoparse() when not available"""
+
+ with subprocess.Popen(["iso8601", "--output-as", "xml", "-d", timestamp],
+ stdout=subprocess.PIPE) as result:
+
+ output = pipe_communicate(result)
+ if result.returncode != 0:
+ raise RuntimeError("Unable to run iso8601 command")
+ match = re.search(r".*\s*(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)Z",
+ output)
+ if match is None:
+ raise RuntimeError("Unable to parse iso8601 command output")
+ return datetime.datetime(int(match.group(1)),
+ int(match.group(2)),
+ int(match.group(3)),
+ int(match.group(4)),
+ int(match.group(5)),
+ int(match.group(6)))
+
+
LOG_WATCHER_BIN = "%s/cts-log-watcher" % BuildOptions.DAEMON_DIR
diff --git a/tools/iso8601.c b/tools/iso8601.c
index 01b9a9238f0..26faba65d54 100644
--- a/tools/iso8601.c
+++ b/tools/iso8601.c
@@ -16,6 +16,13 @@
#define SUMMARY "Display and parse ISO 8601 dates and times"
+static pcmk__supported_format_t formats[] = {
+ PCMK__SUPPORTED_FORMAT_NONE,
+ PCMK__SUPPORTED_FORMAT_TEXT,
+ PCMK__SUPPORTED_FORMAT_XML,
+ { NULL, NULL, NULL }
+};
+
struct {
char *date_time_s;
gchar *duration_s;
@@ -105,26 +112,178 @@ static GOptionEntry modifier_entries[] = {
{ NULL }
};
-static void
-log_time_period(int log_level, crm_time_period_t * dtp, int flags)
+PCMK__OUTPUT_ARGS("date", "const char *", "crm_time_t *", "int")
+static int
+date_default(pcmk__output_t *out, va_list args)
+{
+ const char *prefix = va_arg(args, const char *);
+ crm_time_t *date = va_arg(args, crm_time_t *);
+ int opts = va_arg(args, int);
+
+ char *date_s = NULL;
+
+ opts |= crm_time_log_date | crm_time_log_timeofday;
+ date_s = crm_time_as_string(date, opts);
+
+ out->info(out, "%s: %s", prefix, date_s);
+
+ free(date_s);
+ return pcmk_rc_ok;
+}
+
+PCMK__OUTPUT_ARGS("date", "const char *", "crm_time_t *", "int")
+static int
+date_xml(pcmk__output_t *out, va_list args)
+{
+ const char *prefix G_GNUC_UNUSED = va_arg(args, const char *);
+ crm_time_t *date = va_arg(args, crm_time_t *);
+ int opts = va_arg(args, int);
+
+ char *date_s = NULL;
+
+ opts |= crm_time_log_date | crm_time_log_timeofday;
+ date_s = crm_time_as_string(date, opts);
+
+ pcmk__output_create_xml_text_node(out, PCMK_XE_DATE, date_s);
+ free(date_s);
+ return pcmk_rc_ok;
+}
+
+PCMK__OUTPUT_ARGS("duration", "crm_time_t *", "int")
+static int
+duration_default(pcmk__output_t *out, va_list args)
+{
+ crm_time_t *time = va_arg(args, crm_time_t *);
+ int opts = va_arg(args, int);
+
+ char *date_s = crm_time_as_string(time, opts | crm_time_log_duration);
+
+ out->info(out, "Duration: %s", date_s);
+
+ free(date_s);
+ return pcmk_rc_ok;
+}
+
+PCMK__OUTPUT_ARGS("duration", "crm_time_t *", "int")
+static int
+duration_xml(pcmk__output_t *out, va_list args)
+{
+ crm_time_t *time = va_arg(args, crm_time_t *);
+ int opts = va_arg(args, int);
+
+ char *date_s = crm_time_as_string(time, opts | crm_time_log_duration);
+
+ pcmk__output_create_xml_text_node(out, PCMK_XE_DURATION, date_s);
+ free(date_s);
+ return pcmk_rc_ok;
+}
+
+PCMK__OUTPUT_ARGS("duration_ends", "crm_time_t *", "int")
+static int
+duration_ends_default(pcmk__output_t *out, va_list args)
+{
+ crm_time_t *time = va_arg(args, crm_time_t *);
+ int opts = va_arg(args, int);
+
+ char *date_s = NULL;
+
+ opts |= crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone;
+ date_s = crm_time_as_string(time, opts);
+
+ out->info(out, "Duration ends at: %s", date_s);
+
+ free(date_s);
+ return pcmk_rc_ok;
+}
+
+PCMK__OUTPUT_ARGS("duration_ends", "crm_time_t *", "int")
+static int
+duration_ends_xml(pcmk__output_t *out, va_list args)
+{
+ crm_time_t *time = va_arg(args, crm_time_t *);
+ int opts = va_arg(args, int);
+
+ char *date_s = NULL;
+
+ opts |= crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone;
+ date_s = crm_time_as_string(time, opts);
+
+ pcmk__output_create_xml_text_node(out, PCMK_XE_DURATION_ENDS, date_s);
+ free(date_s);
+ return pcmk_rc_ok;
+}
+
+PCMK__OUTPUT_ARGS("period", "crm_time_period_t *", "int")
+static int
+period_default(pcmk__output_t *out, va_list args)
{
- char *start = crm_time_as_string(dtp->start, flags);
- char *end = crm_time_as_string(dtp->end, flags);
+ crm_time_period_t *period = va_arg(args, crm_time_period_t *);
+ int opts = va_arg(args, int);
+
+ char *start = NULL;
+ char *end = NULL;
+
+ opts |= crm_time_log_date | crm_time_log_timeofday;
+
+ start = crm_time_as_string(period->start, opts);
+ if (start == NULL) {
+ return pcmk_rc_no_output;
+ }
+
+ end = crm_time_as_string(period->end, opts);
+ if (end == NULL) {
+ free(start);
+ return pcmk_rc_no_output;
+ }
+
+ out->info(out, "Period: %s to %s", start, end);
- pcmk__assert(start != NULL && end != NULL);
- do_crm_log(log_level, "Period: %s to %s", start, end);
free(start);
free(end);
+ return pcmk_rc_ok;
+}
+
+PCMK__OUTPUT_ARGS("period", "crm_time_period_t *", "int")
+static int
+period_xml(pcmk__output_t *out, va_list args)
+{
+ crm_time_period_t *period = va_arg(args, crm_time_period_t *);
+ int opts = va_arg(args, int);
+
+ char *start = NULL;
+ char *end = NULL;
+
+ opts |= crm_time_log_date | crm_time_log_timeofday;
+
+ start = crm_time_as_string(period->start, opts);
+ if (start == NULL) {
+ return pcmk_rc_no_output;
+ }
+
+ end = crm_time_as_string(period->end, opts);
+ if (end == NULL) {
+ free(start);
+ return pcmk_rc_no_output;
+ }
+
+ pcmk__output_xml_create_parent(out, PCMK_XE_PERIOD, NULL);
+ pcmk__output_create_xml_text_node(out, PCMK_XE_START, start);
+ pcmk__output_create_xml_text_node(out, PCMK_XE_END, end);
+
+ free(start);
+ free(end);
+ return pcmk_rc_ok;
}
static GOptionContext *
-build_arg_context(pcmk__common_args_t *args) {
+build_arg_context(pcmk__common_args_t *args, GOptionGroup **group)
+{
GOptionContext *context = NULL;
const char *description = "For more information on the ISO 8601 standard, see " \
"https://en.wikipedia.org/wiki/ISO_8601";
- context = pcmk__build_arg_context(args, NULL, NULL, NULL);
+ context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
g_option_context_set_description(context, description);
pcmk__add_arg_group(context, "commands", "Commands:",
@@ -135,19 +294,36 @@ build_arg_context(pcmk__common_args_t *args) {
return context;
}
+static pcmk__message_entry_t fmt_functions[] = {
+ { "date", "default", date_default },
+ { "date", "xml", date_xml },
+ { "duration", "default", duration_default },
+ { "duration", "xml", duration_xml },
+ { "duration_ends", "default", duration_ends_default },
+ { "duration_ends", "xml", duration_ends_xml },
+ { "period", "default", period_default },
+ { "period", "xml", period_xml },
+
+ { NULL, NULL, NULL }
+};
+
int
main(int argc, char **argv)
{
+ int rc = pcmk_rc_ok;
crm_exit_t exit_code = CRM_EX_OK;
crm_time_t *duration = NULL;
crm_time_t *date_time = NULL;
GError *error = NULL;
+ pcmk__output_t *out = NULL;
+ GOptionGroup *output_group = NULL;
pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
- GOptionContext *context = build_arg_context(args);
+ GOptionContext *context = build_arg_context(args, &output_group);
gchar **processed_args = pcmk__cmdline_preproc(argv, "dpDE");
+ pcmk__register_formats(output_group, formats);
if (!g_option_context_parse_strv(context, &processed_args, &error)) {
exit_code = CRM_EX_USAGE;
goto done;
@@ -155,13 +331,22 @@ main(int argc, char **argv)
pcmk__cli_init_logging("iso8601", args->verbosity);
+ rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
+ if (rc != pcmk_rc_ok) {
+ exit_code = pcmk_rc2exitc(rc);
+ g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
+ "Error creating output format %s: %s", args->output_ty,
+ pcmk_rc_str(rc));
+ goto done;
+ }
+
if (args->version) {
- g_strfreev(processed_args);
- pcmk__free_arg_context(context);
- /* FIXME: When iso8601 is converted to use formatted output, this can go. */
- pcmk__cli_help('v');
+ out->version(out, false);
+ goto done;
}
+ pcmk__register_messages(out, fmt_functions);
+
if (pcmk__str_eq("now", options.date_time_s, pcmk__str_casei)) {
date_time = crm_time_new(NULL);
@@ -172,10 +357,8 @@ main(int argc, char **argv)
goto done;
}
- crm_time_log(LOG_TRACE, "Current date/time", date_time,
- crm_time_ordinal | crm_time_log_date | crm_time_log_timeofday);
- crm_time_log(LOG_STDOUT, "Current date/time", date_time,
- options.print_options | crm_time_log_date | crm_time_log_timeofday);
+ out->message(out, "date", "Current date/time", date_time,
+ options.print_options);
} else if (options.date_time_s) {
date_time = crm_time_new(options.date_time_s);
@@ -187,10 +370,7 @@ main(int argc, char **argv)
goto done;
}
- crm_time_log(LOG_TRACE, "Date", date_time,
- crm_time_ordinal | crm_time_log_date | crm_time_log_timeofday);
- crm_time_log(LOG_STDOUT, "Date", date_time,
- options.print_options | crm_time_log_date | crm_time_log_timeofday);
+ out->message(out, "date", "Date", date_time, options.print_options);
}
if (options.duration_s) {
@@ -203,9 +383,7 @@ main(int argc, char **argv)
goto done;
}
- crm_time_log(LOG_TRACE, "Duration", duration, crm_time_log_duration);
- crm_time_log(LOG_STDOUT, "Duration", duration,
- options.print_options | crm_time_log_duration);
+ out->message(out, "duration", duration, options.print_options);
}
if (options.period_s) {
@@ -218,10 +396,7 @@ main(int argc, char **argv)
goto done;
}
- log_time_period(LOG_TRACE, period,
- options.print_options | crm_time_log_date | crm_time_log_timeofday);
- log_time_period(LOG_STDOUT, period,
- options.print_options | crm_time_log_date | crm_time_log_timeofday);
+ out->message(out, "period", period, options.print_options);
crm_time_free_period(period);
}
@@ -236,11 +411,7 @@ main(int argc, char **argv)
goto done;
}
- crm_time_log(LOG_TRACE, "Duration ends at", later,
- crm_time_ordinal | crm_time_log_date | crm_time_log_timeofday);
- crm_time_log(LOG_STDOUT, "Duration ends at", later,
- options.print_options | crm_time_log_date | crm_time_log_timeofday |
- crm_time_log_with_timezone);
+ out->message(out, "duration_ends", later, options.print_options);
if (options.expected_s) {
char *dt_s = crm_time_as_string(later,
@@ -277,6 +448,13 @@ main(int argc, char **argv)
g_free(options.expected_s);
g_free(options.period_s);
- pcmk__output_and_clear_error(&error, NULL);
+ pcmk__output_and_clear_error(&error, out);
+
+ if (out != NULL) {
+ out->finish(out, exit_code, true, NULL);
+ pcmk__output_free(out);
+ }
+
+ pcmk__unregister_formats();
crm_exit(exit_code);
}
diff --git a/xml/Makefile.am b/xml/Makefile.am
index 670a8564290..5d3b0f358ad 100644
--- a/xml/Makefile.am
+++ b/xml/Makefile.am
@@ -66,6 +66,7 @@ API_request_base = command-output \
crm_ticket \
crmadmin \
digests \
+ iso8601 \
pacemakerd \
stonith_admin \
version
diff --git a/xml/api/iso8601-2.38.rng b/xml/api/iso8601-2.38.rng
new file mode 100644
index 00000000000..a60e51c279d
--- /dev/null
+++ b/xml/api/iso8601-2.38.rng
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+