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 @@ + + + + + + + + + + + + + + + + + + + + +