Compare commits
10 Commits
bfcc5c9b0a
...
0c49ffe494
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c49ffe494 | ||
|
|
419aa72eea | ||
|
|
5dce7844a6 | ||
|
|
5d28f055e3 | ||
|
|
8ee5c270c3 | ||
|
|
6e1a15d55b | ||
|
|
1674bd0653 | ||
|
|
a03ab49a0e | ||
|
|
7d20b281a4 | ||
|
|
0b9032d8ef |
58
Export-rule-constraints-in-a-non-deprecated-way.patch
Normal file
58
Export-rule-constraints-in-a-non-deprecated-way.patch
Normal file
@ -0,0 +1,58 @@
|
||||
From 4d375004dd11b7ddc4dd3f211c06008d71626dcf Mon Sep 17 00:00:00 2001
|
||||
From: Tomas Jelinek <tojeline@redhat.com>
|
||||
Date: Tue, 30 Apr 2024 15:31:06 +0200
|
||||
Subject: [PATCH] export rule constraints in a non-deprecated way
|
||||
|
||||
---
|
||||
pcs/cli/constraint/output/location.py | 4 ++--
|
||||
pcs_test/tier1/constraint/test_config.py | 8 ++++----
|
||||
2 files changed, 6 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/pcs/cli/constraint/output/location.py b/pcs/cli/constraint/output/location.py
|
||||
index 25ac646a..141959d5 100644
|
||||
--- a/pcs/cli/constraint/output/location.py
|
||||
+++ b/pcs/cli/constraint/output/location.py
|
||||
@@ -190,7 +190,7 @@ def _add_rule_cmd(constraint_id: str, rule: CibRuleExpressionDto) -> list[str]:
|
||||
indent(
|
||||
[
|
||||
pairs_to_cmd([("id", rule.id)] + _rule_to_cmd_pairs(rule)),
|
||||
- shlex.join(shlex.split(rule.as_string)),
|
||||
+ shlex.quote(rule.as_string),
|
||||
],
|
||||
indent_step=INDENT_STEP,
|
||||
)
|
||||
@@ -221,7 +221,7 @@ def _plain_constraint_rule_to_cmd(
|
||||
+ _attributes_to_pairs(constraint_dto.attributes)
|
||||
+ _rule_to_cmd_pairs(first_rule)
|
||||
),
|
||||
- shlex.join(shlex.split(first_rule.as_string)),
|
||||
+ shlex.quote(first_rule.as_string),
|
||||
],
|
||||
indent_step=INDENT_STEP,
|
||||
)
|
||||
diff --git a/pcs_test/tier1/constraint/test_config.py b/pcs_test/tier1/constraint/test_config.py
|
||||
index 1ce5a2a5..de39b3a5 100644
|
||||
--- a/pcs_test/tier1/constraint/test_config.py
|
||||
+++ b/pcs_test/tier1/constraint/test_config.py
|
||||
@@ -191,14 +191,14 @@ class ConstraintConfigCmdSpaceInDate(ConstraintConfigCmdMixin, TestCase):
|
||||
(
|
||||
"pcs -- constraint location resource%R1 rule \\\n"
|
||||
" id=location-R1-rule constraint-id=location-R1 score=INFINITY \\\n"
|
||||
- " '#uname' eq node1 and date gt 2023-01-01T12:00 and "
|
||||
+ " '#uname eq node1 and date gt 2023-01-01T12:00 and "
|
||||
"date lt 2023-12-31T12:00 and date in_range 2023-01-01T12:00 "
|
||||
- "to 2023-12-31T12:00;\n"
|
||||
+ "to 2023-12-31T12:00';\n"
|
||||
"pcs -- constraint rule add location-R1 \\\n"
|
||||
" id=location-R1-rule-1 score=INFINITY \\\n"
|
||||
- " '#uname' eq node1 and date gt 2023-01-01T12:00 and "
|
||||
+ " '#uname eq node1 and date gt 2023-01-01T12:00 and "
|
||||
"date lt 2023-12-31T12:00 and date in_range 2023-01-01T12:00 "
|
||||
- "to 2023-12-31T12:00\n"
|
||||
+ "to 2023-12-31T12:00'\n"
|
||||
),
|
||||
)
|
||||
|
||||
--
|
||||
2.25.1
|
||||
|
||||
251
Fix-pcsd-ruby.patch
Normal file
251
Fix-pcsd-ruby.patch
Normal file
@ -0,0 +1,251 @@
|
||||
From 19ce5ec842128637595ecbd5b8e1f9733d0999ce Mon Sep 17 00:00:00 2001
|
||||
From: Miroslav Lisik <mlisik@redhat.com>
|
||||
Date: Mon, 5 Feb 2024 13:33:50 +0100
|
||||
Subject: [PATCH] Fix pcsd ruby
|
||||
|
||||
---
|
||||
configure.ac | 19 ++++++++++++++
|
||||
m4/ac_ruby_gem.m4 | 4 +--
|
||||
pcs_test/smoke.sh.in | 42 +++++++++++++++++++++++++++++-
|
||||
pcsd/pcsd.rb | 12 ++++++---
|
||||
pcsd/{rserver.rb => rserver.rb.in} | 3 ++-
|
||||
5 files changed, 72 insertions(+), 8 deletions(-)
|
||||
rename pcsd/{rserver.rb => rserver.rb.in} (97%)
|
||||
|
||||
diff --git a/configure.ac b/configure.ac
|
||||
index 68de52c..86de294 100644
|
||||
--- a/configure.ac
|
||||
+++ b/configure.ac
|
||||
@@ -399,9 +399,22 @@ AC_DEFUN([PCS_CHECK_GEM], [
|
||||
fi
|
||||
])
|
||||
|
||||
+# PCS_GEM_ACTION([curversion], [op], [cmpversion][, action-if-true] [, action-if-false])
|
||||
+AC_DEFUN([PCS_GEM_ACTION], [
|
||||
+ if test -n "$1"; then
|
||||
+ AC_COMPARE_VERSIONS([$1], [$2], [$3], [$4], [$5])
|
||||
+ else
|
||||
+ true
|
||||
+ $5
|
||||
+ fi
|
||||
+])
|
||||
+
|
||||
PCS_CHECK_GEM([power_assert])
|
||||
PCS_CHECK_GEM([test-unit])
|
||||
|
||||
+RACK_HANDLER="Rackup"
|
||||
+REQUIRE_RACKUP="require 'rackup'"
|
||||
+
|
||||
if test "x$tests_only" != "xyes"; then
|
||||
PCS_CHECK_GEM([backports])
|
||||
PCS_CHECK_GEM([childprocess])
|
||||
@@ -411,14 +424,19 @@ if test "x$tests_only" != "xyes"; then
|
||||
PCS_CHECK_GEM([ruby2_keywords])
|
||||
PCS_CHECK_GEM([mustermann])
|
||||
PCS_CHECK_GEM([rack])
|
||||
+ PCS_GEM_ACTION([$HAVE_RUBYGEM_RACK_VERSION], [<], [3.0], [RACK_HANDLER=Rack REQUIRE_RACKUP=""], [PCS_CHECK_GEM([rackup])])
|
||||
PCS_CHECK_GEM([rack-protection])
|
||||
+ PCS_GEM_ACTION([$HAVE_RUBYGEM_RACK_PROTECTION_VERSION], [<], [3.2.0], [], [PCS_CHECK_GEM([base64], [>= 0.1.0])])
|
||||
PCS_CHECK_GEM([rack-test])
|
||||
PCS_CHECK_GEM([sinatra])
|
||||
+ PCS_GEM_ACTION([$HAVE_RUBYGEM_SINATRA_VERSION], [<], [4.0.0], [], [PCS_CHECK_GEM([rack-session], [>= 2.0.0])])
|
||||
PCS_CHECK_GEM([tilt])
|
||||
PCS_CHECK_GEM([nio4r])
|
||||
PCS_CHECK_GEM([puma])
|
||||
PCS_CHECK_GEM([rexml])
|
||||
fi
|
||||
+AC_SUBST([RACK_HANDLER])
|
||||
+AC_SUBST([REQUIRE_RACKUP])
|
||||
|
||||
if test "x$local_build" = "xyes" && test $(wc -l < Gemfile) -gt 2; then
|
||||
GEM_HOME="$LIB_DIR/$PCSD_BUNDLED_DIR_ROOT_LOCAL"
|
||||
@@ -603,6 +621,7 @@ AC_CONFIG_FILES([Makefile
|
||||
pcsd/pcsd-cli.rb
|
||||
pcsd/pcsd-ruby.service
|
||||
pcsd/pcsd.service
|
||||
+ pcsd/rserver.rb
|
||||
pcsd/settings.rb
|
||||
pcsd/logrotate/pcsd])
|
||||
|
||||
diff --git a/m4/ac_ruby_gem.m4 b/m4/ac_ruby_gem.m4
|
||||
index 6e202a3..465084c 100644
|
||||
--- a/m4/ac_ruby_gem.m4
|
||||
+++ b/m4/ac_ruby_gem.m4
|
||||
@@ -29,12 +29,12 @@ AC_DEFUN([AC_RUBY_GEM],[
|
||||
fi
|
||||
AC_MSG_RESULT([yes (detected: $curversion)])
|
||||
eval AS_TR_CPP(HAVE_RUBYGEM_$module)=yes
|
||||
- eval AS_TR_CPP(HAVE_RUBYGEM_$module_version)=$curversion
|
||||
+ eval AS_TR_CPP(HAVE_RUBYGEM_${module}_version)=$curversion
|
||||
$3
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
eval AS_TR_CPP(HAVE_RUBYGEM_$module)=no
|
||||
- eval AS_TR_CPP(HAVE_RUBYGEM_$module_version)=""
|
||||
+ eval AS_TR_CPP(HAVE_RUBYGEM_${module}_version)=""
|
||||
$4
|
||||
fi
|
||||
])
|
||||
diff --git a/pcs_test/smoke.sh.in b/pcs_test/smoke.sh.in
|
||||
index e9466ef..a4b3ac7 100755
|
||||
--- a/pcs_test/smoke.sh.in
|
||||
+++ b/pcs_test/smoke.sh.in
|
||||
@@ -5,7 +5,9 @@ SCRIPT_DIR="$(dirname -- "$(realpath -- "$0")")"
|
||||
|
||||
cluster_user=hacluster
|
||||
cluster_user_password=qa57Jk27eP
|
||||
+cluster_name="cluster-name"
|
||||
pcsd_socket_path="@LOCALSTATEDIR@/run/pcsd.socket"
|
||||
+pcsd_settings_conf_path="@LOCALSTATEDIR@/lib/pcsd/pcs_settings.conf"
|
||||
|
||||
echo "${cluster_user_password}" | passwd --stdin "${cluster_user}";
|
||||
|
||||
@@ -14,11 +16,12 @@ pcs --version
|
||||
pcs host auth localhost --debug -u ${cluster_user} -p ${cluster_user_password}
|
||||
if pidof systemd | grep "\b1\b"; then
|
||||
# this command requires full system with proper init process
|
||||
- pcs cluster setup cluster-name localhost --debug
|
||||
+ pcs cluster setup ${cluster_name} localhost --debug
|
||||
fi
|
||||
|
||||
output_file=$(mktemp)
|
||||
token_file=$(mktemp)
|
||||
+cookie_file=$(mktemp)
|
||||
|
||||
# Sanity check of API V0
|
||||
token=$(python3 -c "import json; print(json.load(open('@LOCALSTATEDIR@/lib/pcsd/known-hosts'))['known_hosts']['localhost']['token']);")
|
||||
@@ -33,6 +36,41 @@ pcs host auth "${custom_localhost_node_name}" addr=localhost --token "${token_fi
|
||||
pcs pcsd accept_token "${token_file}"
|
||||
pcs pcsd status "${custom_localhost_node_name}" | grep "${custom_localhost_node_name}: Online"
|
||||
|
||||
+# Test passing request body (raw_data) in ruby daemon
|
||||
+cat <<EOF > ${pcsd_settings_conf_path}
|
||||
+{
|
||||
+ "format_version": 2,
|
||||
+ "data_version": 1,
|
||||
+ "clusters": [
|
||||
+ {
|
||||
+ "name": "${cluster_name}",
|
||||
+ "nodes": [
|
||||
+ "localhost"
|
||||
+ ]
|
||||
+ }
|
||||
+ ],
|
||||
+ "permissions": {
|
||||
+ "local_cluster": [
|
||||
+ {
|
||||
+ "type": "group",
|
||||
+ "name": "haclient",
|
||||
+ "allow": [
|
||||
+ "grant",
|
||||
+ "read",
|
||||
+ "write"
|
||||
+ ]
|
||||
+ }
|
||||
+ ]
|
||||
+ }
|
||||
+}
|
||||
+EOF
|
||||
+cat ${pcsd_settings_conf_path}
|
||||
+pcs cluster start --all --wait
|
||||
+curl --insecure --data "username=${cluster_user}&password=${cluster_user_password}" --cookie-jar ${cookie_file} https://localhost:2224/ui/login
|
||||
+curl --insecure --cookie ${cookie_file} --header "X-Requested-With: XMLHttpRequest" --data "hidden[hidden_input]=&config[stonith-enabled]=false" https://localhost:2224/managec/${cluster_name}/update_cluster_settings > "${output_file}"
|
||||
+cat "${output_file}"; echo ""
|
||||
+[ "$(cat ${output_file})" = "Update Successful" ]
|
||||
+
|
||||
# Sanity check of API V1
|
||||
curl -kb "token=${token}" https://localhost:2224/api/v1/resource-agent-get-agents-list/v1 --data '{}' > "${output_file}"
|
||||
cat "${output_file}"; echo ""
|
||||
@@ -57,6 +95,8 @@ su testuser -c '! curl --unix-socket '"${pcsd_socket_path}"' http:/something/api
|
||||
# cleanup
|
||||
rm "${token_file}"
|
||||
rm "${output_file}"
|
||||
+rm "${cookie_file}"
|
||||
+rm "${pcsd_settings_conf_path}"
|
||||
pcs cluster destroy --force
|
||||
userdel -r testuser
|
||||
exit 0
|
||||
diff --git a/pcsd/pcsd.rb b/pcsd/pcsd.rb
|
||||
index 2e2baa2..5b8d0a1 100644
|
||||
--- a/pcsd/pcsd.rb
|
||||
+++ b/pcsd/pcsd.rb
|
||||
@@ -848,6 +848,7 @@ end
|
||||
post '/managec/:cluster/api/v1/:command' do
|
||||
auth_user = getAuthUser()
|
||||
if params[:cluster] and params[:command]
|
||||
+ request.body.rewind
|
||||
return send_cluster_request_with_token(
|
||||
auth_user,
|
||||
params[:cluster],
|
||||
@@ -855,7 +856,7 @@ post '/managec/:cluster/api/v1/:command' do
|
||||
true, # post
|
||||
{}, # data - useless when there are raw_data
|
||||
false, # remote
|
||||
- request.env['rack.input'].read # raw_data
|
||||
+ request.body.read # raw_data
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -863,6 +864,7 @@ end
|
||||
get '/managec/:cluster/api/v1/:command' do
|
||||
auth_user = getAuthUser()
|
||||
if params[:cluster] and params[:command]
|
||||
+ request.body.rewind
|
||||
return send_cluster_request_with_token(
|
||||
auth_user,
|
||||
params[:cluster],
|
||||
@@ -870,14 +872,15 @@ get '/managec/:cluster/api/v1/:command' do
|
||||
false, # post
|
||||
{}, # data - useless when there are raw_data
|
||||
false, # remote
|
||||
- request.env['rack.input'].read # raw_data
|
||||
+ request.body.read # raw_data
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
post '/managec/:cluster/?*' do
|
||||
auth_user = getAuthUser()
|
||||
- raw_data = request.env["rack.input"].read
|
||||
+ request.body.rewind
|
||||
+ raw_data = request.body.read
|
||||
if params[:cluster]
|
||||
request = "/" + params[:splat].join("/")
|
||||
|
||||
@@ -889,7 +892,8 @@ end
|
||||
|
||||
get '/managec/:cluster/?*' do
|
||||
auth_user = getAuthUser()
|
||||
- raw_data = request.env["rack.input"].read
|
||||
+ request.body.rewind
|
||||
+ raw_data = request.body.read
|
||||
if params[:cluster]
|
||||
request = "/" + params[:splat].join("/")
|
||||
code, out = send_cluster_request_with_token(
|
||||
diff --git a/pcsd/rserver.rb b/pcsd/rserver.rb.in
|
||||
similarity index 97%
|
||||
rename from pcsd/rserver.rb
|
||||
rename to pcsd/rserver.rb.in
|
||||
index 400df28..02d1a34 100644
|
||||
--- a/pcsd/rserver.rb
|
||||
+++ b/pcsd/rserver.rb.in
|
||||
@@ -2,6 +2,7 @@ require "base64"
|
||||
require "date"
|
||||
require "json"
|
||||
require 'rack'
|
||||
+@REQUIRE_RACKUP@
|
||||
require 'sinatra'
|
||||
|
||||
require 'settings.rb'
|
||||
@@ -59,7 +60,7 @@ use TornadoCommunicationMiddleware
|
||||
|
||||
require 'pcsd'
|
||||
|
||||
-::Rack::Handler.get('puma').run(
|
||||
+::@RACK_HANDLER@::Handler.get('puma').run(
|
||||
Sinatra::Application, :Host => "#{PCSD_RUBY_SOCKET}?umask=0o077", :timeout => 0
|
||||
) do |server|
|
||||
puts server.class
|
||||
--
|
||||
2.41.0
|
||||
|
||||
3141
add-dtos-and-converting-functions-for-resources-stat.patch
Normal file
3141
add-dtos-and-converting-functions-for-resources-stat.patch
Normal file
File diff suppressed because it is too large
Load Diff
243
backport-fix-booth-destroy-for-arbitrators.patch
Normal file
243
backport-fix-booth-destroy-for-arbitrators.patch
Normal file
@ -0,0 +1,243 @@
|
||||
From af9510afb3ce53b3dd05136fdbb9f0a5cc048205 Mon Sep 17 00:00:00 2001
|
||||
From: Tomas Jelinek <tojeline@redhat.com>
|
||||
Date: Fri, 31 May 2024 16:00:06 +0200
|
||||
Subject: [PATCH] fix booth destroy for arbitrators
|
||||
|
||||
---
|
||||
pcs/lib/commands/booth.py | 35 ++++---
|
||||
pcs/lib/pacemaker/live.py | 4 +
|
||||
pcs_test/tier0/lib/commands/test_booth.py | 110 ++++++++++++++++++++--
|
||||
3 files changed, 129 insertions(+), 20 deletions(-)
|
||||
|
||||
diff --git a/pcs/lib/commands/booth.py b/pcs/lib/commands/booth.py
|
||||
index c961705b..f291a085 100644
|
||||
--- a/pcs/lib/commands/booth.py
|
||||
+++ b/pcs/lib/commands/booth.py
|
||||
@@ -58,6 +58,7 @@ from pcs.lib.file.raw_file import (
|
||||
)
|
||||
from pcs.lib.interface.config import ParserErrorException
|
||||
from pcs.lib.node import get_existing_nodes_names
|
||||
+from pcs.lib.pacemaker.live import has_cib_xml
|
||||
from pcs.lib.resource_agent import (
|
||||
ResourceAgentError,
|
||||
ResourceAgentFacade,
|
||||
@@ -165,20 +166,30 @@ def config_destroy(
|
||||
found_instance_name = booth_env.instance_name
|
||||
_ensure_live_env(env, booth_env)
|
||||
|
||||
- booth_resource_list = resource.find_for_config(
|
||||
- get_resources(env.get_cib()),
|
||||
- booth_env.config_path,
|
||||
- )
|
||||
- if booth_resource_list:
|
||||
- report_processor.report(
|
||||
- ReportItem.error(
|
||||
- reports.messages.BoothConfigIsUsed(
|
||||
- found_instance_name,
|
||||
- reports.const.BOOTH_CONFIG_USED_IN_CLUSTER_RESOURCE,
|
||||
- resource_name=str(booth_resource_list[0].get("id", "")),
|
||||
+ if (
|
||||
+ has_cib_xml()
|
||||
+ or env.service_manager.is_running("pacemaker")
|
||||
+ or env.service_manager.is_running("pacemaker_remoted")
|
||||
+ ):
|
||||
+ # To allow destroying booth config on arbitrators, only check CIB if:
|
||||
+ # * pacemaker is running and therefore we are able to get CIB
|
||||
+ # * CIB is stored on disk - pcmk is not running but the node is in a
|
||||
+ # cluster (don't checking corosync to cover remote and guest nodes)
|
||||
+ # If CIB cannot be loaded in either case, fail with an error.
|
||||
+ booth_resource_list = resource.find_for_config(
|
||||
+ get_resources(env.get_cib()),
|
||||
+ booth_env.config_path,
|
||||
+ )
|
||||
+ if booth_resource_list:
|
||||
+ report_processor.report(
|
||||
+ ReportItem.error(
|
||||
+ reports.messages.BoothConfigIsUsed(
|
||||
+ found_instance_name,
|
||||
+ reports.const.BOOTH_CONFIG_USED_IN_CLUSTER_RESOURCE,
|
||||
+ resource_name=str(booth_resource_list[0].get("id", "")),
|
||||
+ )
|
||||
)
|
||||
)
|
||||
- )
|
||||
# Only systemd is currently supported. Initd does not supports multiple
|
||||
# instances (here specified by name)
|
||||
if is_systemd(env.service_manager):
|
||||
diff --git a/pcs/lib/pacemaker/live.py b/pcs/lib/pacemaker/live.py
|
||||
index 301ce343..43197ac1 100644
|
||||
--- a/pcs/lib/pacemaker/live.py
|
||||
+++ b/pcs/lib/pacemaker/live.py
|
||||
@@ -151,6 +151,10 @@ def get_ticket_status_text(runner: CommandRunner) -> Tuple[str, str, int]:
|
||||
### cib
|
||||
|
||||
|
||||
+def has_cib_xml() -> bool:
|
||||
+ return os.path.exists(os.path.join(settings.cib_dir, "cib.xml"))
|
||||
+
|
||||
+
|
||||
def get_cib_xml_cmd_results(
|
||||
runner: CommandRunner, scope: Optional[str] = None
|
||||
) -> tuple[str, str, int]:
|
||||
diff --git a/pcs_test/tier0/lib/commands/test_booth.py b/pcs_test/tier0/lib/commands/test_booth.py
|
||||
index 4e945216..2957e378 100644
|
||||
--- a/pcs_test/tier0/lib/commands/test_booth.py
|
||||
+++ b/pcs_test/tier0/lib/commands/test_booth.py
|
||||
@@ -524,10 +524,13 @@ class ConfigSetupAuthfileFix(TestCase, FixtureMixin):
|
||||
|
||||
|
||||
class ConfigDestroy(TestCase, FixtureMixin):
|
||||
+ # pylint: disable=too-many-public-methods
|
||||
def setUp(self):
|
||||
self.env_assist, self.config = get_env_tools(self)
|
||||
+ self.cib_path = os.path.join(settings.cib_dir, "cib.xml")
|
||||
|
||||
def fixture_config_booth_not_used(self, instance_name="booth"):
|
||||
+ self.config.fs.exists(self.cib_path, True)
|
||||
self.config.runner.cib.load()
|
||||
self.config.services.is_running(
|
||||
"booth", instance=instance_name, return_value=False
|
||||
@@ -536,6 +539,44 @@ class ConfigDestroy(TestCase, FixtureMixin):
|
||||
"booth", instance=instance_name, return_value=False
|
||||
)
|
||||
|
||||
+ def fixture_config_booth_used(
|
||||
+ self,
|
||||
+ instance_name,
|
||||
+ cib_exists=False,
|
||||
+ pcmk_running=False,
|
||||
+ pcmk_remote_running=False,
|
||||
+ booth_running=False,
|
||||
+ booth_enabled=False,
|
||||
+ ):
|
||||
+ cib_load_exception = False
|
||||
+ self.config.fs.exists(self.cib_path, cib_exists)
|
||||
+ if not cib_exists:
|
||||
+ self.config.services.is_running(
|
||||
+ "pacemaker",
|
||||
+ return_value=pcmk_running,
|
||||
+ name="services.is_running.pcmk",
|
||||
+ )
|
||||
+ if not pcmk_running:
|
||||
+ self.config.services.is_running(
|
||||
+ "pacemaker_remoted",
|
||||
+ return_value=pcmk_remote_running,
|
||||
+ name="services.is_running.pcmk_remote",
|
||||
+ )
|
||||
+ if cib_exists and not pcmk_running and not pcmk_remote_running:
|
||||
+ self.config.runner.cib.load(
|
||||
+ returncode=1, stderr="unable to get cib, pcmk is not running"
|
||||
+ )
|
||||
+ cib_load_exception = True
|
||||
+ elif pcmk_running or pcmk_remote_running:
|
||||
+ self.config.runner.cib.load(resources=self.fixture_cib_resources())
|
||||
+ if not cib_load_exception:
|
||||
+ self.config.services.is_running(
|
||||
+ "booth", instance=instance_name, return_value=booth_running
|
||||
+ )
|
||||
+ self.config.services.is_enabled(
|
||||
+ "booth", instance=instance_name, return_value=booth_enabled
|
||||
+ )
|
||||
+
|
||||
def fixture_config_success(self, instance_name="booth"):
|
||||
self.fixture_config_booth_not_used(instance_name)
|
||||
self.config.raw_file.read(
|
||||
@@ -663,17 +704,29 @@ class ConfigDestroy(TestCase, FixtureMixin):
|
||||
expected_in_processor=False,
|
||||
)
|
||||
|
||||
- def test_booth_config_in_use(self):
|
||||
+ def test_booth_config_in_use_cib_pcmk(self):
|
||||
instance_name = "booth"
|
||||
+ self.fixture_config_booth_used(instance_name, pcmk_running=True)
|
||||
|
||||
- self.config.runner.cib.load(resources=self.fixture_cib_resources())
|
||||
- self.config.services.is_running(
|
||||
- "booth", instance=instance_name, return_value=True
|
||||
+ self.env_assist.assert_raise_library_error(
|
||||
+ lambda: commands.config_destroy(self.env_assist.get_env()),
|
||||
)
|
||||
- self.config.services.is_enabled(
|
||||
- "booth", instance=instance_name, return_value=True
|
||||
+
|
||||
+ self.env_assist.assert_reports(
|
||||
+ [
|
||||
+ fixture.error(
|
||||
+ reports.codes.BOOTH_CONFIG_IS_USED,
|
||||
+ name=instance_name,
|
||||
+ detail=reports.const.BOOTH_CONFIG_USED_IN_CLUSTER_RESOURCE,
|
||||
+ resource_name="booth_resource",
|
||||
+ ),
|
||||
+ ]
|
||||
)
|
||||
|
||||
+ def test_booth_config_in_use_cib_pcmk_remote(self):
|
||||
+ instance_name = "booth"
|
||||
+ self.fixture_config_booth_used(instance_name, pcmk_remote_running=True)
|
||||
+
|
||||
self.env_assist.assert_raise_library_error(
|
||||
lambda: commands.config_destroy(self.env_assist.get_env()),
|
||||
)
|
||||
@@ -686,16 +739,57 @@ class ConfigDestroy(TestCase, FixtureMixin):
|
||||
detail=reports.const.BOOTH_CONFIG_USED_IN_CLUSTER_RESOURCE,
|
||||
resource_name="booth_resource",
|
||||
),
|
||||
+ ]
|
||||
+ )
|
||||
+
|
||||
+ def test_pcmk_not_running(self):
|
||||
+ instance_name = "booth"
|
||||
+ self.fixture_config_booth_used(instance_name, cib_exists=True)
|
||||
+
|
||||
+ self.env_assist.assert_raise_library_error(
|
||||
+ lambda: commands.config_destroy(self.env_assist.get_env()),
|
||||
+ [
|
||||
+ fixture.error(
|
||||
+ reports.codes.CIB_LOAD_ERROR,
|
||||
+ reason="unable to get cib, pcmk is not running",
|
||||
+ )
|
||||
+ ],
|
||||
+ expected_in_processor=False,
|
||||
+ )
|
||||
+
|
||||
+ def test_booth_config_in_use_systemd_running(self):
|
||||
+ instance_name = "booth"
|
||||
+ self.fixture_config_booth_used(instance_name, booth_running=True)
|
||||
+
|
||||
+ self.env_assist.assert_raise_library_error(
|
||||
+ lambda: commands.config_destroy(self.env_assist.get_env()),
|
||||
+ )
|
||||
+
|
||||
+ self.env_assist.assert_reports(
|
||||
+ [
|
||||
fixture.error(
|
||||
reports.codes.BOOTH_CONFIG_IS_USED,
|
||||
name=instance_name,
|
||||
- detail=reports.const.BOOTH_CONFIG_USED_ENABLED_IN_SYSTEMD,
|
||||
+ detail=reports.const.BOOTH_CONFIG_USED_RUNNING_IN_SYSTEMD,
|
||||
resource_name=None,
|
||||
),
|
||||
+ ]
|
||||
+ )
|
||||
+
|
||||
+ def test_booth_config_in_use_systemd_enabled(self):
|
||||
+ instance_name = "booth"
|
||||
+ self.fixture_config_booth_used(instance_name, booth_enabled=True)
|
||||
+
|
||||
+ self.env_assist.assert_raise_library_error(
|
||||
+ lambda: commands.config_destroy(self.env_assist.get_env()),
|
||||
+ )
|
||||
+
|
||||
+ self.env_assist.assert_reports(
|
||||
+ [
|
||||
fixture.error(
|
||||
reports.codes.BOOTH_CONFIG_IS_USED,
|
||||
name=instance_name,
|
||||
- detail=reports.const.BOOTH_CONFIG_USED_RUNNING_IN_SYSTEMD,
|
||||
+ detail=reports.const.BOOTH_CONFIG_USED_ENABLED_IN_SYSTEMD,
|
||||
resource_name=None,
|
||||
),
|
||||
]
|
||||
--
|
||||
2.25.1
|
||||
|
||||
49
backport-fix-stdout-wrapping-to-terminal-width.patch
Normal file
49
backport-fix-stdout-wrapping-to-terminal-width.patch
Normal file
@ -0,0 +1,49 @@
|
||||
From 2f4ebe9dfb2d9854e6ae05834e6062d245dae88d Mon Sep 17 00:00:00 2001
|
||||
From: Tomas Jelinek <tojeline@redhat.com>
|
||||
Date: Thu, 16 May 2024 10:36:23 +0200
|
||||
Subject: [PATCH] fix stdout wrapping to terminal width
|
||||
|
||||
---
|
||||
CHANGELOG.md | 3 +++
|
||||
pcs/cli/common/output.py | 8 +++++---
|
||||
2 files changed, 8 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/CHANGELOG.md b/CHANGELOG.md
|
||||
index a198d0f7..a6ef6cc2 100644
|
||||
--- a/CHANGELOG.md
|
||||
+++ b/CHANGELOG.md
|
||||
@@ -7,9 +7,12 @@
|
||||
when not specified in `pcs cluster uidgid add` command. Empty options cause
|
||||
corosync start failure. ([ghissue#772])
|
||||
- Do not allow fencing levels other than 1..9 ([RHEL-2977])
|
||||
+- Do not wrap pcs output to terminal width if pcs's stdout is redirected
|
||||
+ ([RHEL-36514])
|
||||
|
||||
[ghissue#772]: https://github.com/ClusterLabs/pcs/issues/772
|
||||
[RHEL-2977]: https://issues.redhat.com/browse/RHEL-2977
|
||||
+[RHEL-36514]: https://issues.redhat.com/browse/RHEL-36514
|
||||
|
||||
|
||||
## [0.11.7] - 2024-01-11
|
||||
diff --git a/pcs/cli/common/output.py b/pcs/cli/common/output.py
|
||||
index 179f7c03..9dc0e162 100644
|
||||
--- a/pcs/cli/common/output.py
|
||||
+++ b/pcs/cli/common/output.py
|
||||
@@ -56,9 +56,11 @@ def format_wrap_for_terminal(
|
||||
trim -- number which will be substracted from terminal size. Can be used in
|
||||
cases lines will be indented later by this number of spaces.
|
||||
"""
|
||||
- if (sys.stdout is not None and sys.stdout.isatty()) or (
|
||||
- sys.stderr is not None and sys.stderr.isatty()
|
||||
- ):
|
||||
+ # This function is used for stdout only - we don't care about wrapping
|
||||
+ # error messages and debug info. So it checks stdout and not stderr.
|
||||
+ # Checking stderr would enable wrapping in case of 'pcs ... | grep ...'
|
||||
+ # (stderr is connected to a terminal), which we don't want. (RHEL-36514)
|
||||
+ if sys.stdout is not None and sys.stdout.isatty():
|
||||
return format_wrap(
|
||||
text,
|
||||
# minimal line length is 40
|
||||
--
|
||||
2.25.1
|
||||
|
||||
403
fix-do-not-put-empty-uid-gid-options-to-an-uidgid-fi.patch
Normal file
403
fix-do-not-put-empty-uid-gid-options-to-an-uidgid-fi.patch
Normal file
@ -0,0 +1,403 @@
|
||||
From 79a419e033df6c01ffd62eb20feedb01170e1315 Mon Sep 17 00:00:00 2001
|
||||
From: Miroslav Lisik <mlisik@redhat.com>
|
||||
Date: Thu, 25 Jan 2024 15:25:05 +0100
|
||||
Subject: [PATCH] fix: do not put empty uid/gid options to an uidgid file
|
||||
|
||||
* fixed command `pcs cluster uidgid add`
|
||||
|
||||
Resolves #772
|
||||
---
|
||||
CHANGELOG.md | 10 ++
|
||||
pcs/cluster.py | 9 +-
|
||||
pcs/utils.py | 32 +++++-
|
||||
pcs_test/tier1/legacy/test_cluster.py | 154 ++++++++++++++++++++++----
|
||||
4 files changed, 173 insertions(+), 32 deletions(-)
|
||||
|
||||
diff --git a/CHANGELOG.md b/CHANGELOG.md
|
||||
index ee5aab0b..62e7f230 100644
|
||||
--- a/CHANGELOG.md
|
||||
+++ b/CHANGELOG.md
|
||||
@@ -1,5 +1,15 @@
|
||||
# Change Log
|
||||
|
||||
+## [Unreleased]
|
||||
+
|
||||
+### Fixed
|
||||
+- Do not put empty uid/gid corosync configuration options to an uidgid file
|
||||
+ when not specified in `pcs cluster uidgid add` command. Empty options cause
|
||||
+ corosync start failure. ([ghissue#772])
|
||||
+
|
||||
+[ghissue#772]: https://github.com/ClusterLabs/pcs/issues/772
|
||||
+
|
||||
+
|
||||
## [0.11.7] - 2024-01-11
|
||||
|
||||
### Security
|
||||
diff --git a/pcs/cluster.py b/pcs/cluster.py
|
||||
index 01689684..070287c1 100644
|
||||
--- a/pcs/cluster.py
|
||||
+++ b/pcs/cluster.py
|
||||
@@ -1176,8 +1176,8 @@ def cluster_uidgid(
|
||||
del lib
|
||||
modifiers.ensure_only_supported()
|
||||
if not argv:
|
||||
- found = False
|
||||
uid_gid_files = os.listdir(settings.corosync_uidgid_dir)
|
||||
+ uid_gid_lines: list[str] = []
|
||||
for ug_file in uid_gid_files:
|
||||
uid_gid_dict = utils.read_uid_gid_file(ug_file)
|
||||
if "uid" in uid_gid_dict or "gid" in uid_gid_dict:
|
||||
@@ -1188,9 +1188,10 @@ def cluster_uidgid(
|
||||
if "gid" in uid_gid_dict:
|
||||
line += uid_gid_dict["gid"]
|
||||
|
||||
- print(line)
|
||||
- found = True
|
||||
- if not found and not silent_list:
|
||||
+ uid_gid_lines.append(line)
|
||||
+ if uid_gid_lines:
|
||||
+ print("\n".join(sorted(uid_gid_lines)))
|
||||
+ elif not silent_list:
|
||||
print_to_stderr("No uidgids configured")
|
||||
return
|
||||
|
||||
diff --git a/pcs/utils.py b/pcs/utils.py
|
||||
index 44987be1..188f75bc 100644
|
||||
--- a/pcs/utils.py
|
||||
+++ b/pcs/utils.py
|
||||
@@ -16,9 +16,11 @@ import xml.dom.minidom
|
||||
import xml.etree.ElementTree as ET
|
||||
from functools import lru_cache
|
||||
from io import BytesIO
|
||||
+from textwrap import dedent
|
||||
from typing import (
|
||||
Any,
|
||||
Dict,
|
||||
+ Optional,
|
||||
Tuple,
|
||||
)
|
||||
from urllib.parse import urlencode
|
||||
@@ -221,6 +223,25 @@ def read_uid_gid_file(uidgid_filename):
|
||||
return uidgid
|
||||
|
||||
|
||||
+def get_uidgid_file_content(
|
||||
+ uid: Optional[str] = None, gid: Optional[str] = None
|
||||
+) -> Optional[str]:
|
||||
+ if not uid and not gid:
|
||||
+ return None
|
||||
+ uid_gid_lines = []
|
||||
+ if uid:
|
||||
+ uid_gid_lines.append(f" uid: {uid}")
|
||||
+ if gid:
|
||||
+ uid_gid_lines.append(f" gid: {gid}")
|
||||
+ return dedent(
|
||||
+ """\
|
||||
+ uidgid {{
|
||||
+ {uid_gid_keys}
|
||||
+ }}
|
||||
+ """
|
||||
+ ).format(uid_gid_keys="\n".join(uid_gid_lines))
|
||||
+
|
||||
+
|
||||
def write_uid_gid_file(uid, gid):
|
||||
"""
|
||||
Commandline options: no options
|
||||
@@ -237,11 +258,12 @@ def write_uid_gid_file(uid, gid):
|
||||
counter = counter + 1
|
||||
uidgid_filename = orig_filename + "-" + str(counter)
|
||||
|
||||
- data = "uidgid {\n uid: %s\ngid: %s\n}\n" % (uid, gid)
|
||||
- with open(
|
||||
- os.path.join(settings.corosync_uidgid_dir, uidgid_filename), "w"
|
||||
- ) as uidgid_file:
|
||||
- uidgid_file.write(data)
|
||||
+ data = get_uidgid_file_content(uid, gid)
|
||||
+ if data:
|
||||
+ with open(
|
||||
+ os.path.join(settings.corosync_uidgid_dir, uidgid_filename), "w"
|
||||
+ ) as uidgid_file:
|
||||
+ uidgid_file.write(data)
|
||||
|
||||
|
||||
def find_uid_gid_files(uid, gid):
|
||||
diff --git a/pcs_test/tier1/legacy/test_cluster.py b/pcs_test/tier1/legacy/test_cluster.py
|
||||
index 6e72fe80..03064000 100644
|
||||
--- a/pcs_test/tier1/legacy/test_cluster.py
|
||||
+++ b/pcs_test/tier1/legacy/test_cluster.py
|
||||
@@ -1,3 +1,4 @@
|
||||
+import os
|
||||
from functools import partial
|
||||
from unittest import TestCase
|
||||
|
||||
@@ -6,6 +7,8 @@ from pcs_test.tools.misc import get_test_resource as rc
|
||||
from pcs_test.tools.misc import (
|
||||
get_tmp_dir,
|
||||
get_tmp_file,
|
||||
+ outdent,
|
||||
+ read_test_resource,
|
||||
skip_unless_root,
|
||||
write_file_to_tmpfile,
|
||||
)
|
||||
@@ -18,28 +21,40 @@ from pcs_test.tools.pcs_runner import (
|
||||
class UidGidTest(TestCase):
|
||||
def setUp(self):
|
||||
self.uid_gid_dir = get_tmp_dir("tier1_cluster_uidgid")
|
||||
+ self._pcs = partial(
|
||||
+ pcs,
|
||||
+ None,
|
||||
+ mock_settings={"corosync_uidgid_dir": self.uid_gid_dir.name},
|
||||
+ )
|
||||
|
||||
def tearDown(self):
|
||||
self.uid_gid_dir.cleanup()
|
||||
|
||||
+ def assert_uidgid_file_content(self, filename, content):
|
||||
+ self.assertEqual(
|
||||
+ read_test_resource(os.path.join(self.uid_gid_dir.name, filename)),
|
||||
+ content,
|
||||
+ )
|
||||
+
|
||||
+ def assert_uidgid_file_removed(self, filename):
|
||||
+ self.assertEqual(
|
||||
+ os.path.isfile(os.path.join(self.uid_gid_dir.name, filename)),
|
||||
+ False,
|
||||
+ )
|
||||
+
|
||||
def test_uidgid(self):
|
||||
# pylint: disable=too-many-statements
|
||||
- _pcs = partial(
|
||||
- pcs,
|
||||
- None,
|
||||
- mock_settings={"corosync_uidgid_dir": self.uid_gid_dir.name},
|
||||
- )
|
||||
- stdout, stderr, retval = _pcs("cluster uidgid".split())
|
||||
+ stdout, stderr, retval = self._pcs("cluster uidgid".split())
|
||||
self.assertEqual(stdout, "")
|
||||
self.assertEqual(stderr, "No uidgids configured\n")
|
||||
self.assertEqual(retval, 0)
|
||||
|
||||
- stdout, stderr, retval = _pcs("cluster uidgid add".split())
|
||||
+ stdout, stderr, retval = self._pcs("cluster uidgid add".split())
|
||||
self.assertEqual(stdout, "")
|
||||
self.assertTrue(stderr.startswith("\nUsage:"))
|
||||
self.assertEqual(retval, 1)
|
||||
|
||||
- stdout, stderr, retval = _pcs("cluster uidgid rm".split())
|
||||
+ stdout, stderr, retval = self._pcs("cluster uidgid rm".split())
|
||||
self.assertEqual(stdout, "")
|
||||
self.assertTrue(
|
||||
stderr.startswith(
|
||||
@@ -49,19 +64,30 @@ class UidGidTest(TestCase):
|
||||
)
|
||||
self.assertEqual(retval, 1)
|
||||
|
||||
- stdout, stderr, retval = _pcs("cluster uidgid xx".split())
|
||||
+ stdout, stderr, retval = self._pcs("cluster uidgid xx".split())
|
||||
self.assertEqual(stdout, "")
|
||||
self.assertTrue(stderr.startswith("\nUsage:"))
|
||||
self.assertEqual(retval, 1)
|
||||
|
||||
- stdout, stderr, retval = _pcs(
|
||||
+ stdout, stderr, retval = self._pcs(
|
||||
"cluster uidgid add uid=testuid gid=testgid".split()
|
||||
)
|
||||
self.assertEqual(stdout, "")
|
||||
self.assertEqual(stderr, "")
|
||||
self.assertEqual(retval, 0)
|
||||
+ self.assert_uidgid_file_content(
|
||||
+ "pcs-uidgid-testuid-testgid",
|
||||
+ outdent(
|
||||
+ """\
|
||||
+ uidgid {
|
||||
+ uid: testuid
|
||||
+ gid: testgid
|
||||
+ }
|
||||
+ """
|
||||
+ ),
|
||||
+ )
|
||||
|
||||
- stdout, stderr, retval = _pcs(
|
||||
+ stdout, stderr, retval = self._pcs(
|
||||
"cluster uidgid add uid=testuid gid=testgid".split()
|
||||
)
|
||||
self.assertEqual(stdout, "")
|
||||
@@ -72,7 +98,7 @@ class UidGidTest(TestCase):
|
||||
)
|
||||
self.assertEqual(retval, 1)
|
||||
|
||||
- stdout, stderr, retval = _pcs(
|
||||
+ stdout, stderr, retval = self._pcs(
|
||||
"cluster uidgid delete uid=testuid2 gid=testgid2".split()
|
||||
)
|
||||
self.assertEqual(stdout, "")
|
||||
@@ -82,7 +108,7 @@ class UidGidTest(TestCase):
|
||||
)
|
||||
self.assertEqual(retval, 1)
|
||||
|
||||
- stdout, stderr, retval = _pcs(
|
||||
+ stdout, stderr, retval = self._pcs(
|
||||
"cluster uidgid remove uid=testuid gid=testgid2".split()
|
||||
)
|
||||
self.assertEqual(stdout, "")
|
||||
@@ -92,7 +118,7 @@ class UidGidTest(TestCase):
|
||||
)
|
||||
self.assertEqual(retval, 1)
|
||||
|
||||
- stdout, stderr, retval = _pcs(
|
||||
+ stdout, stderr, retval = self._pcs(
|
||||
"cluster uidgid rm uid=testuid2 gid=testgid".split()
|
||||
)
|
||||
self.assertEqual(stdout, "")
|
||||
@@ -104,57 +130,139 @@ class UidGidTest(TestCase):
|
||||
)
|
||||
self.assertEqual(retval, 1)
|
||||
|
||||
- stdout, stderr, retval = _pcs("cluster uidgid".split())
|
||||
+ stdout, stderr, retval = self._pcs("cluster uidgid".split())
|
||||
self.assertEqual(stdout, "UID/GID: uid=testuid gid=testgid\n")
|
||||
self.assertEqual(stderr, "")
|
||||
self.assertEqual(retval, 0)
|
||||
|
||||
- stdout, stderr, retval = _pcs(
|
||||
+ stdout, stderr, retval = self._pcs(
|
||||
"cluster uidgid delete uid=testuid gid=testgid".split()
|
||||
)
|
||||
self.assertEqual(stdout, "")
|
||||
self.assertEqual(stderr, "")
|
||||
self.assertEqual(retval, 0)
|
||||
+ self.assert_uidgid_file_removed("pcs-uidgid-testuid-testgid")
|
||||
|
||||
- stdout, stderr, retval = _pcs(
|
||||
+ stdout, stderr, retval = self._pcs(
|
||||
"cluster uidgid add uid=testuid gid=testgid".split()
|
||||
)
|
||||
self.assertEqual(stdout, "")
|
||||
self.assertEqual(stderr, "")
|
||||
self.assertEqual(retval, 0)
|
||||
|
||||
- stdout, stderr, retval = _pcs("cluster uidgid".split())
|
||||
+ stdout, stderr, retval = self._pcs("cluster uidgid".split())
|
||||
self.assertEqual(stdout, "UID/GID: uid=testuid gid=testgid\n")
|
||||
self.assertEqual(stderr, "")
|
||||
self.assertEqual(retval, 0)
|
||||
|
||||
- stdout, stderr, retval = _pcs(
|
||||
+ stdout, stderr, retval = self._pcs(
|
||||
"cluster uidgid remove uid=testuid gid=testgid".split()
|
||||
)
|
||||
self.assertEqual(stdout, "")
|
||||
self.assertEqual(stderr, "")
|
||||
self.assertEqual(retval, 0)
|
||||
|
||||
- stdout, stderr, retval = _pcs(
|
||||
+ stdout, stderr, retval = self._pcs(
|
||||
"cluster uidgid add uid=testuid gid=testgid".split()
|
||||
)
|
||||
self.assertEqual(stdout, "")
|
||||
self.assertEqual(stderr, "")
|
||||
self.assertEqual(retval, 0)
|
||||
+ self.assert_uidgid_file_content(
|
||||
+ "pcs-uidgid-testuid-testgid",
|
||||
+ outdent(
|
||||
+ """\
|
||||
+ uidgid {
|
||||
+ uid: testuid
|
||||
+ gid: testgid
|
||||
+ }
|
||||
+ """
|
||||
+ ),
|
||||
+ )
|
||||
|
||||
- stdout, stderr, retval = _pcs("cluster uidgid".split())
|
||||
+ stdout, stderr, retval = self._pcs("cluster uidgid".split())
|
||||
self.assertEqual(stdout, "UID/GID: uid=testuid gid=testgid\n")
|
||||
self.assertEqual(stderr, "")
|
||||
self.assertEqual(retval, 0)
|
||||
|
||||
- stdout, stderr, retval = _pcs(
|
||||
+ stdout, stderr, retval = self._pcs(
|
||||
"cluster uidgid delete uid=testuid gid=testgid".split()
|
||||
)
|
||||
self.assertEqual(stdout, "")
|
||||
self.assertEqual(stderr, "")
|
||||
self.assertEqual(retval, 0)
|
||||
+ self.assert_uidgid_file_removed("pcs-uidgid-testuid-testgid")
|
||||
+
|
||||
+ stdout, stderr, retval = self._pcs("cluster uidgid".split())
|
||||
+ self.assertEqual(stdout, "")
|
||||
+ self.assertEqual(stderr, "No uidgids configured\n")
|
||||
+ self.assertEqual(retval, 0)
|
||||
+
|
||||
+ def test_missing_uid_gid(self):
|
||||
+ stdout, stderr, retval = self._pcs(
|
||||
+ "cluster uidgid add uid=1000".split()
|
||||
+ )
|
||||
+ self.assertEqual(stdout, "")
|
||||
+ self.assertEqual(stderr, "")
|
||||
+ self.assertEqual(retval, 0)
|
||||
+ self.assert_uidgid_file_content(
|
||||
+ "pcs-uidgid-1000-",
|
||||
+ outdent(
|
||||
+ """\
|
||||
+ uidgid {
|
||||
+ uid: 1000
|
||||
+ }
|
||||
+ """
|
||||
+ ),
|
||||
+ )
|
||||
+
|
||||
+ stdout, stderr, retval = self._pcs(
|
||||
+ "cluster uidgid add gid=1001".split()
|
||||
+ )
|
||||
+ self.assertEqual(stdout, "")
|
||||
+ self.assertEqual(stderr, "")
|
||||
+ self.assertEqual(retval, 0)
|
||||
+ self.assert_uidgid_file_content(
|
||||
+ "pcs-uidgid--1001",
|
||||
+ outdent(
|
||||
+ """\
|
||||
+ uidgid {
|
||||
+ gid: 1001
|
||||
+ }
|
||||
+ """
|
||||
+ ),
|
||||
+ )
|
||||
+
|
||||
+ stdout, stderr, retval = self._pcs("cluster uidgid".split())
|
||||
+ self.assertEqual(
|
||||
+ stdout,
|
||||
+ outdent(
|
||||
+ """\
|
||||
+ UID/GID: uid= gid=1001
|
||||
+ UID/GID: uid=1000 gid=
|
||||
+ """
|
||||
+ ),
|
||||
+ )
|
||||
+ self.assertEqual(stderr, "")
|
||||
+ self.assertEqual(retval, 0)
|
||||
+
|
||||
+ stdout, stderr, retval = self._pcs(
|
||||
+ "cluster uidgid delete uid=1000".split()
|
||||
+ )
|
||||
+ self.assertEqual(stdout, "")
|
||||
+ self.assertEqual(stderr, "")
|
||||
+ self.assertEqual(retval, 0)
|
||||
+ self.assert_uidgid_file_removed("pcs-uidgid-1000-")
|
||||
+
|
||||
+ stdout, stderr, retval = self._pcs(
|
||||
+ "cluster uidgid delete gid=1001".split()
|
||||
+ )
|
||||
+ self.assertEqual(stdout, "")
|
||||
+ self.assertEqual(stderr, "")
|
||||
+ self.assertEqual(retval, 0)
|
||||
+ self.assert_uidgid_file_removed("pcs-uidgid--1001")
|
||||
|
||||
- stdout, stderr, retval = _pcs("cluster uidgid".split())
|
||||
+ stdout, stderr, retval = self._pcs("cluster uidgid".split())
|
||||
self.assertEqual(stdout, "")
|
||||
self.assertEqual(stderr, "No uidgids configured\n")
|
||||
self.assertEqual(retval, 0)
|
||||
--
|
||||
2.25.1
|
||||
|
||||
268
fix-stonith-level-validation.patch
Normal file
268
fix-stonith-level-validation.patch
Normal file
@ -0,0 +1,268 @@
|
||||
From 88ecf0a5728a39f430bb7f709f4c2b6aa59b583e Mon Sep 17 00:00:00 2001
|
||||
From: Tomas Jelinek <tojeline@redhat.com>
|
||||
Date: Wed, 31 Jan 2024 10:39:12 +0100
|
||||
Subject: [PATCH] fix stonith level validation
|
||||
|
||||
---
|
||||
CHANGELOG.md | 2 +
|
||||
pcs/lib/cib/fencing_topology.py | 26 ++++-------
|
||||
.../tier0/lib/cib/test_fencing_topology.py | 44 +++++++------------
|
||||
pcs_test/tier1/legacy/test_stonith.py | 24 +++++-----
|
||||
4 files changed, 39 insertions(+), 57 deletions(-)
|
||||
|
||||
diff --git a/CHANGELOG.md b/CHANGELOG.md
|
||||
index 62e7f230..208126ef 100644
|
||||
--- a/CHANGELOG.md
|
||||
+++ b/CHANGELOG.md
|
||||
@@ -6,8 +6,10 @@
|
||||
- Do not put empty uid/gid corosync configuration options to an uidgid file
|
||||
when not specified in `pcs cluster uidgid add` command. Empty options cause
|
||||
corosync start failure. ([ghissue#772])
|
||||
+- Do not allow fencing levels other than 1..9 ([RHEL-2977])
|
||||
|
||||
[ghissue#772]: https://github.com/ClusterLabs/pcs/issues/772
|
||||
+[RHEL-2977]: https://issues.redhat.com/browse/RHEL-2977
|
||||
|
||||
|
||||
## [0.11.7] - 2024-01-11
|
||||
diff --git a/pcs/lib/cib/fencing_topology.py b/pcs/lib/cib/fencing_topology.py
|
||||
index 6a9fc71e..2434723b 100644
|
||||
--- a/pcs/lib/cib/fencing_topology.py
|
||||
+++ b/pcs/lib/cib/fencing_topology.py
|
||||
@@ -2,7 +2,6 @@ from typing import (
|
||||
Optional,
|
||||
Sequence,
|
||||
Set,
|
||||
- Tuple,
|
||||
)
|
||||
|
||||
from lxml import etree
|
||||
@@ -23,6 +22,7 @@ from pcs.common.reports import codes as report_codes
|
||||
from pcs.common.reports import has_errors
|
||||
from pcs.common.reports.item import ReportItem
|
||||
from pcs.common.types import StringCollection
|
||||
+from pcs.common.validate import is_integer
|
||||
from pcs.lib.cib.resource.stonith import is_stonith_resource
|
||||
from pcs.lib.cib.tools import find_unique_id
|
||||
from pcs.lib.errors import LibraryError
|
||||
@@ -60,9 +60,8 @@ def add_level(
|
||||
bool force_node -- continue even if a node (target) does not exist
|
||||
"""
|
||||
# pylint: disable=too-many-arguments
|
||||
- report_list, valid_level = _validate_level(level)
|
||||
reporter.report_list(
|
||||
- report_list
|
||||
+ _validate_level(level)
|
||||
+ _validate_target(
|
||||
cluster_status_nodes, target_type, target_value, force_node
|
||||
)
|
||||
@@ -78,7 +77,7 @@ def add_level(
|
||||
if reporter.has_errors:
|
||||
raise LibraryError()
|
||||
_append_level_element(
|
||||
- topology_el, valid_level, target_type, target_value, devices
|
||||
+ topology_el, int(level), target_type, target_value, devices
|
||||
)
|
||||
|
||||
|
||||
@@ -262,22 +261,15 @@ def verify(
|
||||
return report_list
|
||||
|
||||
|
||||
-def _validate_level(level) -> Tuple[ReportItemList, Optional[int]]:
|
||||
+def _validate_level(level) -> ReportItemList:
|
||||
report_list: ReportItemList = []
|
||||
- try:
|
||||
- candidate = int(level)
|
||||
- if candidate > 0:
|
||||
- return report_list, candidate
|
||||
- except ValueError:
|
||||
- pass
|
||||
- report_list.append(
|
||||
- ReportItem.error(
|
||||
- reports.messages.InvalidOptionValue(
|
||||
- "level", level, "a positive integer"
|
||||
+ if not is_integer(level, 1, 9):
|
||||
+ report_list.append(
|
||||
+ ReportItem.error(
|
||||
+ reports.messages.InvalidOptionValue("level", level, "1..9")
|
||||
)
|
||||
)
|
||||
- )
|
||||
- return report_list, None
|
||||
+ return report_list
|
||||
|
||||
|
||||
def _validate_target(
|
||||
diff --git a/pcs_test/tier0/lib/cib/test_fencing_topology.py b/pcs_test/tier0/lib/cib/test_fencing_topology.py
|
||||
index f00d6f31..7c16e15d 100644
|
||||
--- a/pcs_test/tier0/lib/cib/test_fencing_topology.py
|
||||
+++ b/pcs_test/tier0/lib/cib/test_fencing_topology.py
|
||||
@@ -105,7 +105,7 @@ class StatusNodesMixin:
|
||||
@patch_lib("_validate_level_target_devices_does_not_exist", return_value=[])
|
||||
@patch_lib("_validate_devices", return_value=[])
|
||||
@patch_lib("_validate_target", return_value=[])
|
||||
-@patch_lib("_validate_level", return_value=([], "valid_level"))
|
||||
+@patch_lib("_validate_level", return_value=[])
|
||||
class AddLevel(TestCase):
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
def setUp(self):
|
||||
@@ -193,6 +193,7 @@ class AddLevel(TestCase):
|
||||
mock_val_dupl,
|
||||
mock_append,
|
||||
):
|
||||
+ self.level = 1
|
||||
lib.add_level(
|
||||
self.reporter,
|
||||
self.topology_el,
|
||||
@@ -210,7 +211,7 @@ class AddLevel(TestCase):
|
||||
)
|
||||
mock_append.assert_called_once_with(
|
||||
self.topology_el,
|
||||
- "valid_level",
|
||||
+ self.level,
|
||||
self.target_type,
|
||||
self.target_value,
|
||||
self.devices,
|
||||
@@ -224,22 +225,17 @@ class AddLevel(TestCase):
|
||||
mock_val_dupl,
|
||||
mock_append,
|
||||
):
|
||||
- mock_val_level.return_value = (
|
||||
- [
|
||||
- reports.item.ReportItem.error(
|
||||
- reports.messages.InvalidOptionValue(
|
||||
- "level", self.level, "a positive integer"
|
||||
- )
|
||||
- )
|
||||
- ],
|
||||
- None,
|
||||
- )
|
||||
+ mock_val_level.return_value = [
|
||||
+ reports.item.ReportItem.error(
|
||||
+ reports.messages.InvalidOptionValue("level", self.level, "1..9")
|
||||
+ )
|
||||
+ ]
|
||||
report_list = [
|
||||
fixture.error(
|
||||
report_codes.INVALID_OPTION_VALUE,
|
||||
option_value=self.level,
|
||||
option_name="level",
|
||||
- allowed_values="a positive integer",
|
||||
+ allowed_values="1..9",
|
||||
cannot_be_empty=False,
|
||||
forbidden_characters=None,
|
||||
),
|
||||
@@ -746,25 +742,17 @@ class Verify(TestCase, CibMixin, StatusNodesMixin):
|
||||
|
||||
class ValidateLevel(TestCase):
|
||||
def test_success(self):
|
||||
- level_list = [
|
||||
- (1, 1),
|
||||
- ("1", 1),
|
||||
- (9, 9),
|
||||
- ("9", 9),
|
||||
- ("05", 5),
|
||||
- ]
|
||||
- for level_in, level_out in level_list:
|
||||
- with self.subTest(level=level_in):
|
||||
- report_list, valid_level = lib._validate_level(level_in)
|
||||
- self.assertEqual(level_out, valid_level)
|
||||
+ level_list = [1, "1", 9, "9", "05"]
|
||||
+ for level in level_list:
|
||||
+ with self.subTest(level=level):
|
||||
+ report_list = lib._validate_level(level)
|
||||
assert_report_item_list_equal(report_list, [])
|
||||
|
||||
def test_invalid(self):
|
||||
- level_list = ["", 0, "0", -1, "-1", "1abc"]
|
||||
+ level_list = ["", 0, "0", -1, "-1", "1abc", "10"]
|
||||
for level in level_list:
|
||||
with self.subTest(level=level):
|
||||
- report_list, valid_level = lib._validate_level(level)
|
||||
- self.assertEqual(None, valid_level)
|
||||
+ report_list = lib._validate_level(level)
|
||||
assert_report_item_list_equal(
|
||||
report_list,
|
||||
[
|
||||
@@ -772,7 +760,7 @@ class ValidateLevel(TestCase):
|
||||
report_codes.INVALID_OPTION_VALUE,
|
||||
option_value=level,
|
||||
option_name="level",
|
||||
- allowed_values="a positive integer",
|
||||
+ allowed_values="1..9",
|
||||
cannot_be_empty=False,
|
||||
forbidden_characters=None,
|
||||
),
|
||||
diff --git a/pcs_test/tier1/legacy/test_stonith.py b/pcs_test/tier1/legacy/test_stonith.py
|
||||
index 76dd11c3..b293f1a7 100644
|
||||
--- a/pcs_test/tier1/legacy/test_stonith.py
|
||||
+++ b/pcs_test/tier1/legacy/test_stonith.py
|
||||
@@ -2227,36 +2227,36 @@ class LevelAdd(LevelTestsBase):
|
||||
self.assert_pcs_fail(
|
||||
"stonith level add NaN rh7-1 F1".split(),
|
||||
(
|
||||
- "Error: 'NaN' is not a valid level value, use a positive "
|
||||
- "integer\n" + ERRORS_HAVE_OCCURRED
|
||||
+ "Error: 'NaN' is not a valid level value, use 1..9\n"
|
||||
+ + ERRORS_HAVE_OCCURRED
|
||||
),
|
||||
)
|
||||
self.assert_pcs_fail(
|
||||
"-- stonith level add -10 rh7-1 F1".split(),
|
||||
(
|
||||
- "Error: '-10' is not a valid level value, use a positive "
|
||||
- "integer\n" + ERRORS_HAVE_OCCURRED
|
||||
+ "Error: '-10' is not a valid level value, use 1..9\n"
|
||||
+ + ERRORS_HAVE_OCCURRED
|
||||
),
|
||||
)
|
||||
self.assert_pcs_fail(
|
||||
"stonith level add 10abc rh7-1 F1".split(),
|
||||
(
|
||||
- "Error: '10abc' is not a valid level value, use a positive "
|
||||
- "integer\n" + ERRORS_HAVE_OCCURRED
|
||||
+ "Error: '10abc' is not a valid level value, use 1..9\n"
|
||||
+ + ERRORS_HAVE_OCCURRED
|
||||
),
|
||||
)
|
||||
self.assert_pcs_fail(
|
||||
"stonith level add 0 rh7-1 F1".split(),
|
||||
(
|
||||
- "Error: '0' is not a valid level value, use a positive "
|
||||
- "integer\n" + ERRORS_HAVE_OCCURRED
|
||||
+ "Error: '0' is not a valid level value, use 1..9\n"
|
||||
+ + ERRORS_HAVE_OCCURRED
|
||||
),
|
||||
)
|
||||
self.assert_pcs_fail(
|
||||
"stonith level add 000 rh7-1 F1".split(),
|
||||
(
|
||||
- "Error: '000' is not a valid level value, use a positive "
|
||||
- "integer\n" + ERRORS_HAVE_OCCURRED
|
||||
+ "Error: '000' is not a valid level value, use 1..9\n"
|
||||
+ + ERRORS_HAVE_OCCURRED
|
||||
),
|
||||
)
|
||||
|
||||
@@ -2273,7 +2273,7 @@ class LevelAdd(LevelTestsBase):
|
||||
self.assert_pcs_fail(
|
||||
"stonith level add x rh7-X F0 dev@ce".split(),
|
||||
(
|
||||
- "Error: 'x' is not a valid level value, use a positive integer\n"
|
||||
+ "Error: 'x' is not a valid level value, use 1..9\n"
|
||||
"Error: Node 'rh7-X' does not appear to exist in configuration, "
|
||||
"use --force to override\n"
|
||||
"Error: invalid device id 'dev@ce', '@' is not a valid character "
|
||||
@@ -2286,7 +2286,7 @@ class LevelAdd(LevelTestsBase):
|
||||
self.assert_pcs_fail(
|
||||
"stonith level add x rh7-X F0 dev@ce --force".split(),
|
||||
(
|
||||
- "Error: 'x' is not a valid level value, use a positive integer\n"
|
||||
+ "Error: 'x' is not a valid level value, use 1..9\n"
|
||||
"Warning: Node 'rh7-X' does not appear to exist in configuration\n"
|
||||
"Error: invalid device id 'dev@ce', '@' is not a valid character "
|
||||
"for a device id\n"
|
||||
--
|
||||
2.25.1
|
||||
|
||||
3129
fixes-after-review.patch
Normal file
3129
fixes-after-review.patch
Normal file
File diff suppressed because it is too large
Load Diff
28
increase-a-timeout-in-a-test.patch
Normal file
28
increase-a-timeout-in-a-test.patch
Normal file
@ -0,0 +1,28 @@
|
||||
From 78cfa76f7edbe362c152d2ad4ad8e4012a61e437 Mon Sep 17 00:00:00 2001
|
||||
From: Tomas Jelinek <tojeline@redhat.com>
|
||||
Date: Wed, 17 Apr 2024 17:25:04 +0200
|
||||
Subject: [PATCH] increase a timeout in a test
|
||||
|
||||
---
|
||||
pcs_test/tier0/daemon/app/test_app_remote.py | 5 ++++-
|
||||
1 file changed, 4 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/pcs_test/tier0/daemon/app/test_app_remote.py b/pcs_test/tier0/daemon/app/test_app_remote.py
|
||||
index c6a6b235..dc176846 100644
|
||||
--- a/pcs_test/tier0/daemon/app/test_app_remote.py
|
||||
+++ b/pcs_test/tier0/daemon/app/test_app_remote.py
|
||||
@@ -121,7 +121,10 @@ class SyncConfigMutualExclusive(AppTest):
|
||||
# Without lock the timeout should be enough to finish task. With the
|
||||
# lock it should raise because of timeout. The same timeout is used for
|
||||
# noticing differences between test with and test without lock.
|
||||
- return self.io_loop.run_sync(fetch_sync_options, timeout=0.5)
|
||||
+ # The timeout needs to be long enough for the test to fit into it even
|
||||
+ # if running on a slower machine. And it should be short enough not to
|
||||
+ # make the test run unnecessary long.
|
||||
+ return self.io_loop.run_sync(fetch_sync_options, timeout=2.5)
|
||||
|
||||
def check_call_wrapper_without_lock(self, method):
|
||||
self.assert_wrappers_response(self.fetch_set_sync_options(method))
|
||||
--
|
||||
2.33.0
|
||||
|
||||
65
pcs.spec
65
pcs.spec
@ -1,6 +1,6 @@
|
||||
Name: pcs
|
||||
Version: 0.11.7
|
||||
Release: 1
|
||||
Release: 15
|
||||
License: GPL-2.0-only AND Apache-2.0 AND MIT AND BSD-3-Clause AND (BSD-2-Clause OR Ruby) AND (BSD-2-Clause OR GPL-2.0-or-later)
|
||||
URL: https://github.com/ClusterLabs/pcs
|
||||
Group: System Environment/Base
|
||||
@ -38,12 +38,20 @@ Source4: https://github.com/ClusterLabs/pcs-web-ui/releases/download/%{ui_commit
|
||||
# Patch0: bzNUMBER-01-name.patch
|
||||
Patch0: do-not-support-cluster-setup-with-udp-u-transport.patch
|
||||
Patch1: Support-for-openEuler.patch
|
||||
|
||||
Patch2: fix-do-not-put-empty-uid-gid-options-to-an-uidgid-fi.patch
|
||||
Patch3: fix-stonith-level-validation.patch
|
||||
Patch4: Fix-pcsd-ruby.patch
|
||||
Patch5: update-crm_mon-schemas-for-tests.patch
|
||||
Patch6: add-dtos-and-converting-functions-for-resources-stat.patch
|
||||
Patch7: fixes-after-review.patch
|
||||
Patch8: store-clone-instance-id-in-resource-status-dtos.patch
|
||||
Patch9: increase-a-timeout-in-a-test.patch
|
||||
Patch10: Export-rule-constraints-in-a-non-deprecated-way.patch
|
||||
Patch11: backport-fix-stdout-wrapping-to-terminal-width.patch
|
||||
Patch12: backport-fix-booth-destroy-for-arbitrators.patch
|
||||
# ui patches: >200
|
||||
# Patch201: bzNUMBER-01-name.patch
|
||||
|
||||
# git for patches
|
||||
BuildRequires: git-core
|
||||
BuildRequires: make
|
||||
# printf from coreutils is used in makefile, head is used in spec
|
||||
BuildRequires: coreutils
|
||||
@ -179,6 +187,9 @@ Provides: bundled(pyagentx) = %{pyagentx_version}
|
||||
SNMP agent that provides information about pacemaker cluster to the master agent (snmpd)
|
||||
|
||||
%prep
|
||||
%if "%{_vendor}" != "openEuler"
|
||||
sed -i 's/openEuler/%{_vendor}/g' %{PATCH1}
|
||||
%endif
|
||||
|
||||
# -- following is inspired by python-simplejon.el5 --
|
||||
# Update timestamps on the files touched by a patch, to avoid non-equal
|
||||
@ -223,12 +234,12 @@ update_times_patch(){
|
||||
# * http://ftp.rpm.org/max-rpm/s1-rpm-inside-macros.html
|
||||
# * https://rpm-software-management.github.io/rpm/manual/autosetup.html
|
||||
# patch web-ui sources
|
||||
%autosetup -D -T -b 3 -a 4 -S git -n %{ui_src_name} -N
|
||||
%autosetup -D -T -b 3 -a 4 -n %{ui_src_name} -N
|
||||
%autopatch -p1 -m 201
|
||||
# update_times_patch %%{PATCH201}
|
||||
|
||||
# patch pcs sources
|
||||
%autosetup -S git -n %{pcs_source_name} -N
|
||||
%autosetup -n %{pcs_source_name} -N
|
||||
%autopatch -p1 -m 0
|
||||
# update_times_patch %%{PATCH0}
|
||||
update_times_patch %{PATCH0}
|
||||
@ -398,6 +409,48 @@ run_all_tests
|
||||
%license pyagentx_LICENSE.txt
|
||||
|
||||
%changelog
|
||||
* Mon Sep 02 2024 zouzhimin <zouzhimin@kylinos.cn> - 0.11.7-15
|
||||
- fix booth destroy for arbitrators
|
||||
|
||||
* Wed May 29 2024 zouzhimin <zouzhimin@kylinos.cn> - 0.11.7-14
|
||||
- fix stdout wrapping to terminal width and modify spec file, sed command to replace "patch3" with "patch1"
|
||||
|
||||
* Mon May 13 2024 zouzhimin <zouzhimin@kylinos.cn> - 0.11.7-13
|
||||
- fix: Support for other distributions and delete -S git from %autosetup
|
||||
|
||||
* Sat May 11 2024 bixiaoyan <bixiaoyan@kylinos.cn> - 0.11.7-12
|
||||
- export rule constraints in a non-deprecated way
|
||||
|
||||
* Wed Apr 24 2024 bizhiyuan <bizhiyuan@kylinos.cn> - 0.11.7-11
|
||||
- increase a timeout in a test
|
||||
|
||||
* Mon Apr 22 2024 laokz <zhangkai@iscas.ac.cn> - 0.11.7-10
|
||||
- restore setuptools-scm name to adapt setuptools-68.0.0
|
||||
|
||||
* Tue Mar 26 2024 zouzhimin <zouzhimin@kylinos.cn> - 0.11.7-9
|
||||
- Add dtos for resources status
|
||||
|
||||
* Mon Mar 25 2024 zouzhimin <zouzhimin@kylinos.cn> - 0.11.7-8
|
||||
- fixes after review
|
||||
|
||||
* Fri Mar 22 2024 zouzhimin <zouzhimin@kylinos.cn> - 0.11.7-7
|
||||
- add dtos and converting functions for resources status
|
||||
|
||||
* Tue Mar 19 2024 zouzhimin <zouzhimin@kylinos.cn> - 0.11.7-6
|
||||
- update crm_mon schemas for tests
|
||||
|
||||
* Tue Mar 19 2024 panchenbo <panchenbo@kylinsec.com.cn> - 0.11.7-5
|
||||
- fix setuptools_scm not found
|
||||
|
||||
* Tue Mar 12 2024 zouzhimin <zouzhimin@kylinos.cn> - 0.11.7-4
|
||||
- Fix pcsd ruby
|
||||
|
||||
* Mon Mar 04 2024 zouzhimin <zouzhimin@kylinos.cn> - 0.11.7-3
|
||||
- fix stonith level validation
|
||||
|
||||
* Fri Mar 01 2024 zouzhimin <zouzhimin@kylinos.cn> - 0.11.7-2
|
||||
- fixed command `pcs cluster uidgid add`
|
||||
|
||||
* Tue Feb 27 2024 zouzhimin <zouzhimin@kylinos.cn> - 0.11.7-1
|
||||
- Update package to version 0.11.7
|
||||
|
||||
|
||||
812
store-clone-instance-id-in-resource-status-dtos.patch
Normal file
812
store-clone-instance-id-in-resource-status-dtos.patch
Normal file
@ -0,0 +1,812 @@
|
||||
From 7c56001aa76c4a5f69f29b328061c419c7ce856b Mon Sep 17 00:00:00 2001
|
||||
From: Peter Romancik <promanci@redhat.com>
|
||||
Date: Thu, 1 Feb 2024 17:17:40 +0100
|
||||
Subject: [PATCH 1/2] further fixes after review
|
||||
|
||||
---
|
||||
pcs/common/reports/codes.py | 2 +-
|
||||
pcs/common/reports/messages.py | 8 +-
|
||||
pcs/lib/pacemaker/status.py | 85 ++++++++++---------
|
||||
.../tier0/common/reports/test_messages.py | 14 +--
|
||||
pcs_test/tier0/lib/commands/test_status.py | 4 +-
|
||||
pcs_test/tier0/lib/pacemaker/test_status.py | 68 ++++++++-------
|
||||
6 files changed, 97 insertions(+), 84 deletions(-)
|
||||
|
||||
diff --git a/pcs/common/reports/codes.py b/pcs/common/reports/codes.py
|
||||
index f9614331..e967d0b1 100644
|
||||
--- a/pcs/common/reports/codes.py
|
||||
+++ b/pcs/common/reports/codes.py
|
||||
@@ -50,7 +50,7 @@ AGENT_SELF_VALIDATION_SKIPPED_UPDATED_RESOURCE_MISCONFIGURED = M(
|
||||
)
|
||||
AGENT_SELF_VALIDATION_RESULT = M("AGENT_SELF_VALIDATION_RESULT")
|
||||
BAD_CLUSTER_STATE_FORMAT = M("BAD_CLUSTER_STATE_FORMAT")
|
||||
-BAD_CLUSTER_STATE = M("BAD_CLUSTER_STATE")
|
||||
+BAD_CLUSTER_STATE_DATA = M("BAD_CLUSTER_STATE_DATA")
|
||||
BOOTH_ADDRESS_DUPLICATION = M("BOOTH_ADDRESS_DUPLICATION")
|
||||
BOOTH_ALREADY_IN_CIB = M("BOOTH_ALREADY_IN_CIB")
|
||||
BOOTH_AUTHFILE_NOT_USED = M("BOOTH_AUTHFILE_NOT_USED")
|
||||
diff --git a/pcs/common/reports/messages.py b/pcs/common/reports/messages.py
|
||||
index 8b9bc63e..53f15170 100644
|
||||
--- a/pcs/common/reports/messages.py
|
||||
+++ b/pcs/common/reports/messages.py
|
||||
@@ -3277,7 +3277,7 @@ class BadClusterStateFormat(ReportItemMessage):
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
-class BadClusterState(ReportItemMessage):
|
||||
+class BadClusterStateData(ReportItemMessage):
|
||||
"""
|
||||
crm_mon xml output is invalid despite conforming to the schema
|
||||
|
||||
@@ -3285,13 +3285,13 @@ class BadClusterState(ReportItemMessage):
|
||||
"""
|
||||
|
||||
reason: Optional[str] = None
|
||||
- _code = codes.BAD_CLUSTER_STATE
|
||||
+ _code = codes.BAD_CLUSTER_STATE_DATA
|
||||
|
||||
@property
|
||||
def message(self) -> str:
|
||||
return (
|
||||
"Cannot load cluster status, xml does not describe valid cluster "
|
||||
- f"status{format_optional(self.reason, template=': {}')}."
|
||||
+ f"status{format_optional(self.reason, template=': {}')}"
|
||||
)
|
||||
|
||||
|
||||
@@ -3314,7 +3314,7 @@ class ClusterStatusBundleMemberIdAsImplicit(ReportItemMessage):
|
||||
return (
|
||||
"Skipping bundle '{bundle_id}': {resource_word} "
|
||||
"{bad_ids} {has} the same id as some of the "
|
||||
- "implicit bundle resources."
|
||||
+ "implicit bundle resources"
|
||||
).format(
|
||||
bundle_id=self.bundle_id,
|
||||
resource_word=format_plural(self.bad_ids, "resource"),
|
||||
diff --git a/pcs/lib/pacemaker/status.py b/pcs/lib/pacemaker/status.py
|
||||
index a86ede55..deb8aa0d 100644
|
||||
--- a/pcs/lib/pacemaker/status.py
|
||||
+++ b/pcs/lib/pacemaker/status.py
|
||||
@@ -2,7 +2,6 @@ from collections import Counter
|
||||
from typing import (
|
||||
Optional,
|
||||
Sequence,
|
||||
- Union,
|
||||
cast,
|
||||
)
|
||||
|
||||
@@ -60,11 +59,13 @@ class UnexpectedMemberError(ClusterStatusParsingError):
|
||||
resource_id: str,
|
||||
resource_type: str,
|
||||
member_id: str,
|
||||
+ member_type: str,
|
||||
expected_types: list[str],
|
||||
):
|
||||
super().__init__(resource_id)
|
||||
self.resource_type = resource_type
|
||||
self.member_id = member_id
|
||||
+ self.member_type = member_type
|
||||
self.expected_types = expected_types
|
||||
|
||||
|
||||
@@ -106,46 +107,44 @@ def cluster_status_parsing_error_to_report(
|
||||
) -> reports.ReportItem:
|
||||
reason = ""
|
||||
if isinstance(e, EmptyResourceIdError):
|
||||
- reason = "Resource with empty id."
|
||||
+ reason = "Resource with an empty id"
|
||||
elif isinstance(e, EmptyNodeNameError):
|
||||
reason = (
|
||||
- f"Resource with id '{e.resource_id}' contains node with empty name."
|
||||
+ f"Resource '{e.resource_id}' contains a node with an empty name"
|
||||
)
|
||||
elif isinstance(e, UnknownPcmkRoleError):
|
||||
reason = (
|
||||
- f"Resource with id '{e.resource_id}' contains unknown "
|
||||
- f"pcmk role '{e.role}'."
|
||||
+ f"Resource '{e.resource_id}' contains an unknown "
|
||||
+ f"role '{e.role}'"
|
||||
)
|
||||
elif isinstance(e, UnexpectedMemberError):
|
||||
reason = (
|
||||
- f"Unexpected resource '{e.member_id}' inside of resource "
|
||||
- f"'{e.resource_id}' of type '{e.resource_type}'. "
|
||||
- f"Only resources of type {format_list(e.expected_types, '|')} "
|
||||
- f"can be in {e.resource_type}."
|
||||
+ f"Unexpected resource '{e.member_id}' of type '{e.member_type}' "
|
||||
+ f"inside of resource '{e.resource_id}' of type '{e.resource_type}'."
|
||||
+ f" Only resources of type {format_list(e.expected_types)} "
|
||||
+ f"can be in a {e.resource_type}"
|
||||
)
|
||||
|
||||
elif isinstance(e, MixedMembersError):
|
||||
- reason = (
|
||||
- f"Primitive and group members mixed in clone '{e.resource_id}'."
|
||||
- )
|
||||
+ reason = f"Primitive and group members mixed in clone '{e.resource_id}'"
|
||||
elif isinstance(e, DifferentMemberIdsError):
|
||||
- reason = f"Members with different ids in resource '{e.resource_id}'."
|
||||
+ reason = f"Members with different ids in clone '{e.resource_id}'"
|
||||
elif isinstance(e, BundleReplicaMissingImplicitResourceError):
|
||||
reason = (
|
||||
f"Replica '{e.replica_id}' of bundle '{e.resource_id}' "
|
||||
- f"is missing implicit {e.implicit_type} resource."
|
||||
+ f"is missing implicit {e.implicit_type} resource"
|
||||
)
|
||||
elif isinstance(e, BundleReplicaInvalidMemberCountError):
|
||||
reason = (
|
||||
f"Replica '{e.replica_id}' of bundle '{e.resource_id}' has "
|
||||
- "invalid number of members."
|
||||
+ "invalid number of members"
|
||||
)
|
||||
elif isinstance(e, BundleDifferentReplicas):
|
||||
- reason = f"Replicas of bundle '{e.resource_id}' are not the same."
|
||||
+ reason = f"Replicas of bundle '{e.resource_id}' are not the same"
|
||||
|
||||
return reports.ReportItem(
|
||||
reports.ReportItemSeverity.error(),
|
||||
- reports.messages.BadClusterState(reason),
|
||||
+ reports.messages.BadClusterStateData(reason),
|
||||
)
|
||||
|
||||
|
||||
@@ -160,7 +159,7 @@ def _primitive_to_dto(
|
||||
target_role = _get_target_role(primitive_el)
|
||||
|
||||
node_names = [
|
||||
- str(node.get("name")) for node in primitive_el.iterfind("node")
|
||||
+ str(node.attrib["name"]) for node in primitive_el.iterfind("node")
|
||||
]
|
||||
|
||||
if node_names and any(not name for name in node_names):
|
||||
@@ -168,7 +167,7 @@ def _primitive_to_dto(
|
||||
|
||||
return PrimitiveStatusDto(
|
||||
resource_id,
|
||||
- str(primitive_el.get("resource_agent")),
|
||||
+ str(primitive_el.attrib["resource_agent"]),
|
||||
role,
|
||||
target_role,
|
||||
is_true(primitive_el.get("active", "false")),
|
||||
@@ -179,7 +178,7 @@ def _primitive_to_dto(
|
||||
is_true(primitive_el.get("failed", "false")),
|
||||
is_true(primitive_el.get("managed", "false")),
|
||||
is_true(primitive_el.get("failure_ignored", "false")),
|
||||
- [str(node.get("name")) for node in primitive_el.iterfind("node")],
|
||||
+ node_names,
|
||||
primitive_el.get("pending"),
|
||||
primitive_el.get("locked_to"),
|
||||
)
|
||||
@@ -197,7 +196,11 @@ def _group_to_dto(
|
||||
member_list.append(_primitive_to_dto(member, remove_clone_suffix))
|
||||
else:
|
||||
raise UnexpectedMemberError(
|
||||
- group_id, "group", str(member.get("id")), ["primitive"]
|
||||
+ group_id,
|
||||
+ "group",
|
||||
+ str(member.attrib["id"]),
|
||||
+ member.tag,
|
||||
+ ["primitive"],
|
||||
)
|
||||
|
||||
return GroupStatusDto(
|
||||
@@ -228,29 +231,28 @@ def _clone_to_dto(
|
||||
group_list.append(_group_to_dto(member, is_unique))
|
||||
else:
|
||||
raise UnexpectedMemberError(
|
||||
- clone_id, "clone", str(member.get("id")), ["primitive", "group"]
|
||||
+ clone_id,
|
||||
+ "clone",
|
||||
+ str(member.attrib["id"]),
|
||||
+ member.tag,
|
||||
+ ["primitive", "group"],
|
||||
)
|
||||
|
||||
if primitive_list and group_list:
|
||||
raise MixedMembersError(clone_id)
|
||||
|
||||
- instance_list: Union[list[PrimitiveStatusDto], list[GroupStatusDto]]
|
||||
if primitive_list:
|
||||
if len(set(res.resource_id for res in primitive_list)) > 1:
|
||||
raise DifferentMemberIdsError(clone_id)
|
||||
- instance_list = primitive_list
|
||||
- else:
|
||||
+ if group_list:
|
||||
group_ids = set(group.resource_id for group in group_list)
|
||||
children_ids = set(
|
||||
tuple(child.resource_id for child in group.members)
|
||||
for group in group_list
|
||||
)
|
||||
-
|
||||
if len(group_ids) > 1 or len(children_ids) > 1:
|
||||
raise DifferentMemberIdsError(clone_id)
|
||||
|
||||
- instance_list = group_list
|
||||
-
|
||||
return CloneStatusDto(
|
||||
clone_id,
|
||||
is_true(clone_el.get("multi_state", "false")),
|
||||
@@ -262,7 +264,7 @@ def _clone_to_dto(
|
||||
is_true(clone_el.get("failed", "false")),
|
||||
is_true(clone_el.get("failure_ignored", "false")),
|
||||
target_role,
|
||||
- instance_list,
|
||||
+ primitive_list or group_list,
|
||||
)
|
||||
|
||||
|
||||
@@ -270,7 +272,7 @@ def _bundle_to_dto(
|
||||
bundle_el: _Element, _remove_clone_suffix: bool = False
|
||||
) -> BundleStatusDto:
|
||||
bundle_id = _get_resource_id(bundle_el)
|
||||
- bundle_type = str(bundle_el.get("type"))
|
||||
+ bundle_type = str(bundle_el.attrib["type"])
|
||||
|
||||
replica_list = [
|
||||
_replica_to_dto(replica, bundle_id, bundle_type)
|
||||
@@ -283,7 +285,7 @@ def _bundle_to_dto(
|
||||
return BundleStatusDto(
|
||||
bundle_id,
|
||||
bundle_type,
|
||||
- str(bundle_el.get("image")),
|
||||
+ str(bundle_el.attrib["image"]),
|
||||
is_true(bundle_el.get("unique", "false")),
|
||||
is_true(bundle_el.get("maintenance", "false")),
|
||||
bundle_el.get("description"),
|
||||
@@ -302,17 +304,18 @@ class ClusterStatusParser:
|
||||
}
|
||||
|
||||
def __init__(self, status: _Element):
|
||||
- self.status = status
|
||||
- self.warnings: reports.ReportItemList = []
|
||||
+ """
|
||||
+ status -- xml element from crm_mon xml, validated using the appropriate
|
||||
+ rng schema
|
||||
+ """
|
||||
+ self._status = status
|
||||
+ self._warnings: reports.ReportItemList = []
|
||||
|
||||
def status_xml_to_dto(self) -> ResourcesStatusDto:
|
||||
"""
|
||||
Return dto containing status of configured resources in the cluster
|
||||
-
|
||||
- status -- status xml document from crm_mon, validated using
|
||||
- the appropriate rng schema
|
||||
"""
|
||||
- resource_list = cast(list[_Element], self.status.xpath("resources/*"))
|
||||
+ resource_list = cast(list[_Element], self._status.xpath("resources/*"))
|
||||
|
||||
resource_dto_list = []
|
||||
for resource in resource_list:
|
||||
@@ -328,7 +331,7 @@ class ClusterStatusParser:
|
||||
# the implicitly created resource.
|
||||
# We only skip such bundles while still providing status of the
|
||||
# other resources.
|
||||
- self.warnings.append(
|
||||
+ self._warnings.append(
|
||||
reports.ReportItem.warning(
|
||||
reports.messages.ClusterStatusBundleMemberIdAsImplicit(
|
||||
e.bundle_id, e.bad_ids
|
||||
@@ -339,11 +342,11 @@ class ClusterStatusParser:
|
||||
return ResourcesStatusDto(resource_dto_list)
|
||||
|
||||
def get_warnings(self) -> reports.ReportItemList:
|
||||
- return self.warnings
|
||||
+ return self._warnings
|
||||
|
||||
|
||||
def _get_resource_id(resource: _Element) -> str:
|
||||
- resource_id = resource.get("id")
|
||||
+ resource_id = resource.attrib["id"]
|
||||
if not resource_id:
|
||||
raise EmptyResourceIdError()
|
||||
return str(resource_id)
|
||||
@@ -374,7 +377,7 @@ def _remove_clone_suffix(resource_id: str) -> str:
|
||||
def _replica_to_dto(
|
||||
replica_el: _Element, bundle_id: str, bundle_type: str
|
||||
) -> BundleReplicaStatusDto:
|
||||
- replica_id = str(replica_el.get("id"))
|
||||
+ replica_id = str(replica_el.attrib["id"])
|
||||
|
||||
resource_list = [
|
||||
_primitive_to_dto(resource)
|
||||
diff --git a/pcs_test/tier0/common/reports/test_messages.py b/pcs_test/tier0/common/reports/test_messages.py
|
||||
index 48eb730c..0ca95920 100644
|
||||
--- a/pcs_test/tier0/common/reports/test_messages.py
|
||||
+++ b/pcs_test/tier0/common/reports/test_messages.py
|
||||
@@ -2195,23 +2195,23 @@ class BadClusterStateFormat(NameBuildTest):
|
||||
)
|
||||
|
||||
|
||||
-class BadClusterState(NameBuildTest):
|
||||
+class BadClusterStateData(NameBuildTest):
|
||||
def test_no_reason(self):
|
||||
self.assert_message_from_report(
|
||||
(
|
||||
"Cannot load cluster status, xml does not describe "
|
||||
- "valid cluster status."
|
||||
+ "valid cluster status"
|
||||
),
|
||||
- reports.BadClusterState(),
|
||||
+ reports.BadClusterStateData(),
|
||||
)
|
||||
|
||||
def test_reason(self):
|
||||
self.assert_message_from_report(
|
||||
(
|
||||
"Cannot load cluster status, xml does not describe "
|
||||
- "valid cluster status: sample reason."
|
||||
+ "valid cluster status: sample reason"
|
||||
),
|
||||
- reports.BadClusterState("sample reason"),
|
||||
+ reports.BadClusterStateData("sample reason"),
|
||||
)
|
||||
|
||||
|
||||
@@ -5843,7 +5843,7 @@ class ClusterStatusBundleMemberIdAsImplicit(NameBuildTest):
|
||||
self.assert_message_from_report(
|
||||
(
|
||||
"Skipping bundle 'resource-bundle': resource 'resource' has "
|
||||
- "the same id as some of the implicit bundle resources."
|
||||
+ "the same id as some of the implicit bundle resources"
|
||||
),
|
||||
reports.ClusterStatusBundleMemberIdAsImplicit(
|
||||
"resource-bundle", ["resource"]
|
||||
@@ -5855,7 +5855,7 @@ class ClusterStatusBundleMemberIdAsImplicit(NameBuildTest):
|
||||
(
|
||||
"Skipping bundle 'resource-bundle': resources 'resource-0', "
|
||||
"'resource-1' have the same id as some of the implicit bundle "
|
||||
- "resources."
|
||||
+ "resources"
|
||||
),
|
||||
reports.ClusterStatusBundleMemberIdAsImplicit(
|
||||
"resource-bundle", ["resource-0", "resource-1"]
|
||||
diff --git a/pcs_test/tier0/lib/commands/test_status.py b/pcs_test/tier0/lib/commands/test_status.py
|
||||
index 3b6b7665..b12e9531 100644
|
||||
--- a/pcs_test/tier0/lib/commands/test_status.py
|
||||
+++ b/pcs_test/tier0/lib/commands/test_status.py
|
||||
@@ -1342,8 +1342,8 @@ class ResourcesStatus(TestCase):
|
||||
lambda: status.resources_status(self.env_assist.get_env()),
|
||||
[
|
||||
fixture.error(
|
||||
- report_codes.BAD_CLUSTER_STATE,
|
||||
- reason="Resource with id 'R7' contains unknown pcmk role 'NotPcmkRole'.",
|
||||
+ report_codes.BAD_CLUSTER_STATE_DATA,
|
||||
+ reason="Resource 'R7' contains an unknown role 'NotPcmkRole'",
|
||||
),
|
||||
],
|
||||
False,
|
||||
diff --git a/pcs_test/tier0/lib/pacemaker/test_status.py b/pcs_test/tier0/lib/pacemaker/test_status.py
|
||||
index 778e97a6..ced1a47e 100644
|
||||
--- a/pcs_test/tier0/lib/pacemaker/test_status.py
|
||||
+++ b/pcs_test/tier0/lib/pacemaker/test_status.py
|
||||
@@ -12,6 +12,7 @@ from pcs.common import reports
|
||||
from pcs.common.const import (
|
||||
PCMK_ROLE_STARTED,
|
||||
PCMK_ROLES,
|
||||
+ PCMK_STATUS_ROLE_PROMOTED,
|
||||
PCMK_STATUS_ROLE_STARTED,
|
||||
PCMK_STATUS_ROLE_STOPPED,
|
||||
PCMK_STATUS_ROLE_UNPROMOTED,
|
||||
@@ -334,8 +335,8 @@ class TestParsingErrorToReport(TestCase):
|
||||
assert_report_item_equal(
|
||||
report,
|
||||
fixture.error(
|
||||
- reports.codes.BAD_CLUSTER_STATE,
|
||||
- reason="Resource with empty id.",
|
||||
+ reports.codes.BAD_CLUSTER_STATE_DATA,
|
||||
+ reason="Resource with an empty id",
|
||||
),
|
||||
)
|
||||
|
||||
@@ -346,8 +347,8 @@ class TestParsingErrorToReport(TestCase):
|
||||
assert_report_item_equal(
|
||||
report,
|
||||
fixture.error(
|
||||
- reports.codes.BAD_CLUSTER_STATE,
|
||||
- reason="Resource with id 'resource' contains node with empty name.",
|
||||
+ reports.codes.BAD_CLUSTER_STATE_DATA,
|
||||
+ reason="Resource 'resource' contains a node with an empty name",
|
||||
),
|
||||
)
|
||||
|
||||
@@ -358,25 +359,25 @@ class TestParsingErrorToReport(TestCase):
|
||||
assert_report_item_equal(
|
||||
report,
|
||||
fixture.error(
|
||||
- reports.codes.BAD_CLUSTER_STATE,
|
||||
- reason="Resource with id 'resource' contains unknown pcmk role 'NotPcmkRole'.",
|
||||
+ reports.codes.BAD_CLUSTER_STATE_DATA,
|
||||
+ reason="Resource 'resource' contains an unknown role 'NotPcmkRole'",
|
||||
),
|
||||
)
|
||||
|
||||
def test_unexpected_member_group(self):
|
||||
report = status.cluster_status_parsing_error_to_report(
|
||||
status.UnexpectedMemberError(
|
||||
- "resource", "group", "member", ["primitive"]
|
||||
+ "resource", "group", "member", "bundle", ["primitive"]
|
||||
)
|
||||
)
|
||||
assert_report_item_equal(
|
||||
report,
|
||||
fixture.error(
|
||||
- reports.codes.BAD_CLUSTER_STATE,
|
||||
+ reports.codes.BAD_CLUSTER_STATE_DATA,
|
||||
reason=(
|
||||
- "Unexpected resource 'member' inside of resource "
|
||||
- "'resource' of type 'group'. Only resources of type "
|
||||
- "'primitive' can be in group."
|
||||
+ "Unexpected resource 'member' of type 'bundle' inside of "
|
||||
+ "resource 'resource' of type 'group'. Only resources of "
|
||||
+ "type 'primitive' can be in a group"
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -384,17 +385,17 @@ class TestParsingErrorToReport(TestCase):
|
||||
def test_unexpected_member_clone(self):
|
||||
report = status.cluster_status_parsing_error_to_report(
|
||||
status.UnexpectedMemberError(
|
||||
- "resource", "clone", "member", ["primitive", "group"]
|
||||
+ "resource", "clone", "member", "bundle", ["primitive", "group"]
|
||||
)
|
||||
)
|
||||
assert_report_item_equal(
|
||||
report,
|
||||
fixture.error(
|
||||
- reports.codes.BAD_CLUSTER_STATE,
|
||||
+ reports.codes.BAD_CLUSTER_STATE_DATA,
|
||||
reason=(
|
||||
- "Unexpected resource 'member' inside of resource "
|
||||
- "'resource' of type 'clone'. Only resources of type "
|
||||
- "'group'|'primitive' can be in clone."
|
||||
+ "Unexpected resource 'member' of type 'bundle' inside of "
|
||||
+ "resource 'resource' of type 'clone'. Only resources of "
|
||||
+ "type 'group', 'primitive' can be in a clone"
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -406,8 +407,8 @@ class TestParsingErrorToReport(TestCase):
|
||||
assert_report_item_equal(
|
||||
report,
|
||||
fixture.error(
|
||||
- reports.codes.BAD_CLUSTER_STATE,
|
||||
- reason="Primitive and group members mixed in clone 'resource'.",
|
||||
+ reports.codes.BAD_CLUSTER_STATE_DATA,
|
||||
+ reason="Primitive and group members mixed in clone 'resource'",
|
||||
),
|
||||
)
|
||||
|
||||
@@ -418,8 +419,8 @@ class TestParsingErrorToReport(TestCase):
|
||||
assert_report_item_equal(
|
||||
report,
|
||||
fixture.error(
|
||||
- reports.codes.BAD_CLUSTER_STATE,
|
||||
- reason="Members with different ids in resource 'resource'.",
|
||||
+ reports.codes.BAD_CLUSTER_STATE_DATA,
|
||||
+ reason="Members with different ids in clone 'resource'",
|
||||
),
|
||||
)
|
||||
|
||||
@@ -432,8 +433,8 @@ class TestParsingErrorToReport(TestCase):
|
||||
assert_report_item_equal(
|
||||
report,
|
||||
fixture.error(
|
||||
- reports.codes.BAD_CLUSTER_STATE,
|
||||
- reason="Replica '0' of bundle 'resource' is missing implicit container resource.",
|
||||
+ reports.codes.BAD_CLUSTER_STATE_DATA,
|
||||
+ reason="Replica '0' of bundle 'resource' is missing implicit container resource",
|
||||
),
|
||||
)
|
||||
|
||||
@@ -444,8 +445,8 @@ class TestParsingErrorToReport(TestCase):
|
||||
assert_report_item_equal(
|
||||
report,
|
||||
fixture.error(
|
||||
- reports.codes.BAD_CLUSTER_STATE,
|
||||
- reason="Replica '0' of bundle 'resource' has invalid number of members.",
|
||||
+ reports.codes.BAD_CLUSTER_STATE_DATA,
|
||||
+ reason="Replica '0' of bundle 'resource' has invalid number of members",
|
||||
),
|
||||
)
|
||||
|
||||
@@ -456,8 +457,8 @@ class TestParsingErrorToReport(TestCase):
|
||||
assert_report_item_equal(
|
||||
report,
|
||||
fixture.error(
|
||||
- reports.codes.BAD_CLUSTER_STATE,
|
||||
- reason="Replicas of bundle 'resource' are not the same.",
|
||||
+ reports.codes.BAD_CLUSTER_STATE_DATA,
|
||||
+ reason="Replicas of bundle 'resource' are not the same",
|
||||
),
|
||||
)
|
||||
|
||||
@@ -549,6 +550,7 @@ class TestPrimitiveStatusToDto(TestCase):
|
||||
with self.assertRaises(status.UnknownPcmkRoleError) as cm:
|
||||
status._primitive_to_dto(primitive_xml)
|
||||
self.assertEqual(cm.exception.resource_id, "resource")
|
||||
+ self.assertEqual(cm.exception.role, "NotPcmkRole")
|
||||
|
||||
def test_target_role(self):
|
||||
for role in PCMK_ROLES:
|
||||
@@ -573,6 +575,7 @@ class TestPrimitiveStatusToDto(TestCase):
|
||||
with self.assertRaises(status.UnknownPcmkRoleError) as cm:
|
||||
status._primitive_to_dto(primitive_xml)
|
||||
self.assertEqual(cm.exception.resource_id, "resource")
|
||||
+ self.assertEqual(cm.exception.role, value)
|
||||
|
||||
|
||||
class TestGroupStatusToDto(TestCase):
|
||||
@@ -695,7 +698,11 @@ class TestGroupStatusToDto(TestCase):
|
||||
with self.assertRaises(status.UnexpectedMemberError) as cm:
|
||||
status._group_to_dto(group_xml)
|
||||
self.assertEqual(cm.exception.resource_id, "outer-group")
|
||||
+ self.assertEqual(cm.exception.resource_type, "group")
|
||||
self.assertEqual(cm.exception.member_id, resource_id)
|
||||
+ self.assertEqual(
|
||||
+ cm.exception.member_type, resource_id.split("-")[1]
|
||||
+ )
|
||||
self.assertEqual(cm.exception.expected_types, ["primitive"])
|
||||
|
||||
def test_remove_clone_suffix(self):
|
||||
@@ -796,7 +803,7 @@ class TestCloneStatusToDto(TestCase):
|
||||
fixture_clone_xml(
|
||||
multi_state=True,
|
||||
instances=[
|
||||
- fixture_primitive_xml(role=PCMK_STATUS_ROLE_UNPROMOTED),
|
||||
+ fixture_primitive_xml(role=PCMK_STATUS_ROLE_PROMOTED),
|
||||
fixture_primitive_xml(
|
||||
role=PCMK_STATUS_ROLE_UNPROMOTED, node_names=["node2"]
|
||||
),
|
||||
@@ -810,7 +817,7 @@ class TestCloneStatusToDto(TestCase):
|
||||
fixture_clone_dto(
|
||||
multi_state=True,
|
||||
instances=[
|
||||
- fixture_primitive_dto(role=PCMK_STATUS_ROLE_UNPROMOTED),
|
||||
+ fixture_primitive_dto(role=PCMK_STATUS_ROLE_PROMOTED),
|
||||
fixture_primitive_dto(
|
||||
role=PCMK_STATUS_ROLE_UNPROMOTED, node_names=["node2"]
|
||||
),
|
||||
@@ -1003,9 +1010,12 @@ class TestCloneStatusToDto(TestCase):
|
||||
|
||||
with self.assertRaises(status.UnexpectedMemberError) as cm:
|
||||
status._clone_to_dto(clone_xml)
|
||||
-
|
||||
self.assertEqual(cm.exception.resource_id, "outer-clone")
|
||||
+ self.assertEqual(cm.exception.resource_type, "clone")
|
||||
self.assertEqual(cm.exception.member_id, resource_id)
|
||||
+ self.assertEqual(
|
||||
+ cm.exception.member_type, resource_id.split("-")[1]
|
||||
+ )
|
||||
self.assertEqual(
|
||||
cm.exception.expected_types, ["primitive", "group"]
|
||||
)
|
||||
--
|
||||
2.25.1
|
||||
|
||||
From c32249a39ef262e3f2106eb8ca01b6efb8e74707 Mon Sep 17 00:00:00 2001
|
||||
From: Peter Romancik <promanci@redhat.com>
|
||||
Date: Thu, 1 Feb 2024 17:45:20 +0100
|
||||
Subject: [PATCH 2/2] store clone instance id in resource status dtos
|
||||
|
||||
---
|
||||
pcs/common/status_dto.py | 2 ++
|
||||
pcs/lib/pacemaker/status.py | 19 +++++++----
|
||||
pcs_test/tier0/lib/commands/test_status.py | 3 ++
|
||||
pcs_test/tier0/lib/pacemaker/test_status.py | 36 ++++++++++++++++-----
|
||||
4 files changed, 46 insertions(+), 14 deletions(-)
|
||||
|
||||
diff --git a/pcs/common/status_dto.py b/pcs/common/status_dto.py
|
||||
index dcc94eca..240ff930 100644
|
||||
--- a/pcs/common/status_dto.py
|
||||
+++ b/pcs/common/status_dto.py
|
||||
@@ -16,6 +16,7 @@ from pcs.common.interface.dto import DataTransferObject
|
||||
class PrimitiveStatusDto(DataTransferObject):
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
resource_id: str
|
||||
+ clone_instance_id: Optional[str]
|
||||
resource_agent: str
|
||||
role: PcmkStatusRoleType
|
||||
target_role: Optional[PcmkRoleType]
|
||||
@@ -35,6 +36,7 @@ class PrimitiveStatusDto(DataTransferObject):
|
||||
@dataclass(frozen=True)
|
||||
class GroupStatusDto(DataTransferObject):
|
||||
resource_id: str
|
||||
+ clone_instance_id: Optional[str]
|
||||
maintenance: bool
|
||||
description: Optional[str]
|
||||
managed: bool
|
||||
diff --git a/pcs/lib/pacemaker/status.py b/pcs/lib/pacemaker/status.py
|
||||
index deb8aa0d..6b37d6cb 100644
|
||||
--- a/pcs/lib/pacemaker/status.py
|
||||
+++ b/pcs/lib/pacemaker/status.py
|
||||
@@ -152,8 +152,9 @@ def _primitive_to_dto(
|
||||
primitive_el: _Element, remove_clone_suffix: bool = False
|
||||
) -> PrimitiveStatusDto:
|
||||
resource_id = _get_resource_id(primitive_el)
|
||||
+ clone_suffix = None
|
||||
if remove_clone_suffix:
|
||||
- resource_id = _remove_clone_suffix(resource_id)
|
||||
+ resource_id, clone_suffix = _remove_clone_suffix(resource_id)
|
||||
|
||||
role = _get_role(primitive_el)
|
||||
target_role = _get_target_role(primitive_el)
|
||||
@@ -167,6 +168,7 @@ def _primitive_to_dto(
|
||||
|
||||
return PrimitiveStatusDto(
|
||||
resource_id,
|
||||
+ clone_suffix,
|
||||
str(primitive_el.attrib["resource_agent"]),
|
||||
role,
|
||||
target_role,
|
||||
@@ -187,8 +189,11 @@ def _primitive_to_dto(
|
||||
def _group_to_dto(
|
||||
group_el: _Element, remove_clone_suffix: bool = False
|
||||
) -> GroupStatusDto:
|
||||
- # clone suffix is added even when the clone is non unique
|
||||
- group_id = _remove_clone_suffix(_get_resource_id(group_el))
|
||||
+ # clone instance id present even when the clone is non unique
|
||||
+ group_id, clone_instance_id = _remove_clone_suffix(
|
||||
+ _get_resource_id(group_el)
|
||||
+ )
|
||||
+
|
||||
member_list = []
|
||||
|
||||
for member in group_el:
|
||||
@@ -205,6 +210,7 @@ def _group_to_dto(
|
||||
|
||||
return GroupStatusDto(
|
||||
group_id,
|
||||
+ clone_instance_id,
|
||||
is_true(group_el.get("maintenance", "false")),
|
||||
group_el.get("description"),
|
||||
is_true(group_el.get("managed", "false")),
|
||||
@@ -368,10 +374,11 @@ def _get_target_role(resource: _Element) -> Optional[PcmkRoleType]:
|
||||
return PcmkRoleType(target_role)
|
||||
|
||||
|
||||
-def _remove_clone_suffix(resource_id: str) -> str:
|
||||
+def _remove_clone_suffix(resource_id: str) -> tuple[str, Optional[str]]:
|
||||
if ":" in resource_id:
|
||||
- return resource_id.rsplit(":", 1)[0]
|
||||
- return resource_id
|
||||
+ resource_id, clone_suffix = resource_id.rsplit(":", 1)
|
||||
+ return resource_id, clone_suffix
|
||||
+ return resource_id, None
|
||||
|
||||
|
||||
def _replica_to_dto(
|
||||
diff --git a/pcs_test/tier0/lib/commands/test_status.py b/pcs_test/tier0/lib/commands/test_status.py
|
||||
index b12e9531..c7c808a3 100644
|
||||
--- a/pcs_test/tier0/lib/commands/test_status.py
|
||||
+++ b/pcs_test/tier0/lib/commands/test_status.py
|
||||
@@ -1280,6 +1280,7 @@ def _fixture_primitive_resource_dto(
|
||||
) -> PrimitiveStatusDto:
|
||||
return PrimitiveStatusDto(
|
||||
resource_id=resource_id,
|
||||
+ clone_instance_id=None,
|
||||
resource_agent=resource_agent,
|
||||
role=PCMK_STATUS_ROLE_STOPPED,
|
||||
target_role=target_role,
|
||||
@@ -1448,6 +1449,7 @@ class ResourcesStatus(TestCase):
|
||||
),
|
||||
GroupStatusDto(
|
||||
resource_id="G2",
|
||||
+ clone_instance_id=None,
|
||||
maintenance=False,
|
||||
description=None,
|
||||
managed=True,
|
||||
@@ -1475,6 +1477,7 @@ class ResourcesStatus(TestCase):
|
||||
instances=[
|
||||
GroupStatusDto(
|
||||
resource_id="G1",
|
||||
+ clone_instance_id="0",
|
||||
maintenance=False,
|
||||
description=None,
|
||||
managed=True,
|
||||
diff --git a/pcs_test/tier0/lib/pacemaker/test_status.py b/pcs_test/tier0/lib/pacemaker/test_status.py
|
||||
index ced1a47e..a852d45b 100644
|
||||
--- a/pcs_test/tier0/lib/pacemaker/test_status.py
|
||||
+++ b/pcs_test/tier0/lib/pacemaker/test_status.py
|
||||
@@ -85,6 +85,7 @@ def fixture_primitive_xml(
|
||||
|
||||
def fixture_primitive_dto(
|
||||
resource_id: str = "resource",
|
||||
+ clone_instance_id: Optional[str] = None,
|
||||
resource_agent: str = "ocf:heartbeat:Dummy",
|
||||
role: PcmkStatusRoleType = PCMK_STATUS_ROLE_STARTED,
|
||||
target_role: Optional[str] = None,
|
||||
@@ -94,6 +95,7 @@ def fixture_primitive_dto(
|
||||
) -> PrimitiveStatusDto:
|
||||
return PrimitiveStatusDto(
|
||||
resource_id,
|
||||
+ clone_instance_id,
|
||||
resource_agent,
|
||||
role,
|
||||
target_role,
|
||||
@@ -136,11 +138,13 @@ def fixture_group_xml(
|
||||
|
||||
def fixture_group_dto(
|
||||
resource_id: str = "resource-group",
|
||||
+ clone_instance_id: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
members: Sequence[PrimitiveStatusDto] = (),
|
||||
) -> GroupStatusDto:
|
||||
return GroupStatusDto(
|
||||
resource_id,
|
||||
+ clone_instance_id,
|
||||
maintenance=False,
|
||||
description=description,
|
||||
managed=True,
|
||||
@@ -506,7 +510,7 @@ class TestPrimitiveStatusToDto(TestCase):
|
||||
|
||||
result = status._primitive_to_dto(primitive_xml, True)
|
||||
|
||||
- self.assertEqual(result, fixture_primitive_dto())
|
||||
+ self.assertEqual(result, fixture_primitive_dto(clone_instance_id="0"))
|
||||
|
||||
def test_running_on_multiple_nodes(self):
|
||||
primitive_xml = etree.fromstring(
|
||||
@@ -716,7 +720,10 @@ class TestGroupStatusToDto(TestCase):
|
||||
result = status._group_to_dto(group_xml, True)
|
||||
self.assertEqual(
|
||||
result,
|
||||
- fixture_group_dto(members=[fixture_primitive_dto()]),
|
||||
+ fixture_group_dto(
|
||||
+ clone_instance_id="0",
|
||||
+ members=[fixture_primitive_dto(clone_instance_id="0")],
|
||||
+ ),
|
||||
)
|
||||
|
||||
|
||||
@@ -792,8 +799,10 @@ class TestCloneStatusToDto(TestCase):
|
||||
fixture_clone_dto(
|
||||
unique=True,
|
||||
instances=[
|
||||
- fixture_primitive_dto(),
|
||||
- fixture_primitive_dto(node_names=["node2"]),
|
||||
+ fixture_primitive_dto(clone_instance_id="0"),
|
||||
+ fixture_primitive_dto(
|
||||
+ clone_instance_id="1", node_names=["node2"]
|
||||
+ ),
|
||||
],
|
||||
),
|
||||
)
|
||||
@@ -886,9 +895,12 @@ class TestCloneStatusToDto(TestCase):
|
||||
result,
|
||||
fixture_clone_dto(
|
||||
instances=[
|
||||
- fixture_group_dto(members=[fixture_primitive_dto()]),
|
||||
fixture_group_dto(
|
||||
- members=[fixture_primitive_dto(node_names=["node2"])]
|
||||
+ clone_instance_id="0", members=[fixture_primitive_dto()]
|
||||
+ ),
|
||||
+ fixture_group_dto(
|
||||
+ clone_instance_id="1",
|
||||
+ members=[fixture_primitive_dto(node_names=["node2"])],
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -923,9 +935,17 @@ class TestCloneStatusToDto(TestCase):
|
||||
fixture_clone_dto(
|
||||
unique=True,
|
||||
instances=[
|
||||
- fixture_group_dto(members=[fixture_primitive_dto()]),
|
||||
fixture_group_dto(
|
||||
- members=[fixture_primitive_dto(node_names=["node2"])]
|
||||
+ clone_instance_id="0",
|
||||
+ members=[fixture_primitive_dto(clone_instance_id="0")],
|
||||
+ ),
|
||||
+ fixture_group_dto(
|
||||
+ clone_instance_id="1",
|
||||
+ members=[
|
||||
+ fixture_primitive_dto(
|
||||
+ clone_instance_id="1", node_names=["node2"]
|
||||
+ )
|
||||
+ ],
|
||||
),
|
||||
],
|
||||
),
|
||||
--
|
||||
2.25.1
|
||||
|
||||
957
update-crm_mon-schemas-for-tests.patch
Normal file
957
update-crm_mon-schemas-for-tests.patch
Normal file
@ -0,0 +1,957 @@
|
||||
From 2b7322113eec6a2d789d47d47049ae90c7a46625 Mon Sep 17 00:00:00 2001
|
||||
From: Peter Romancik <promanci@redhat.com>
|
||||
Date: Fri, 2 Feb 2024 10:50:11 +0100
|
||||
Subject: [PATCH] update crm_mon schemas for tests
|
||||
|
||||
---
|
||||
pcs_test/Makefile.am | 11 +-
|
||||
pcs_test/resources/crm_mon.minimal.xml | 4 +-
|
||||
.../resources/pcmk_api_rng/api-result.rng | 2 +-
|
||||
.../resources/pcmk_api_rng/crm_mon-2.29.rng | 213 ++++++++++++
|
||||
.../resources/pcmk_api_rng/crm_mon-2.4.rng | 311 ------------------
|
||||
.../resources/pcmk_api_rng/failure-2.8.rng | 33 ++
|
||||
...nce-event-2.0.rng => fence-event-2.15.rng} | 3 +
|
||||
.../resources/pcmk_api_rng/node-attrs-2.8.rng | 24 ++
|
||||
.../pcmk_api_rng/node-history-2.12.rng | 70 ++++
|
||||
.../resources/pcmk_api_rng/nodes-2.29.rng | 54 +++
|
||||
.../pcmk_api_rng/pacemakerd-health-2.25.rng | 20 ++
|
||||
.../{resources-2.4.rng => resources-2.29.rng} | 45 ++-
|
||||
12 files changed, 472 insertions(+), 318 deletions(-)
|
||||
create mode 100644 pcs_test/resources/pcmk_api_rng/crm_mon-2.29.rng
|
||||
delete mode 100644 pcs_test/resources/pcmk_api_rng/crm_mon-2.4.rng
|
||||
create mode 100644 pcs_test/resources/pcmk_api_rng/failure-2.8.rng
|
||||
rename pcs_test/resources/pcmk_api_rng/{fence-event-2.0.rng => fence-event-2.15.rng} (91%)
|
||||
create mode 100644 pcs_test/resources/pcmk_api_rng/node-attrs-2.8.rng
|
||||
create mode 100644 pcs_test/resources/pcmk_api_rng/node-history-2.12.rng
|
||||
create mode 100644 pcs_test/resources/pcmk_api_rng/nodes-2.29.rng
|
||||
create mode 100644 pcs_test/resources/pcmk_api_rng/pacemakerd-health-2.25.rng
|
||||
rename pcs_test/resources/pcmk_api_rng/{resources-2.4.rng => resources-2.29.rng} (76%)
|
||||
|
||||
diff --git a/pcs_test/Makefile.am b/pcs_test/Makefile.am
|
||||
index 94e4c07a..32ac5eee 100644
|
||||
--- a/pcs_test/Makefile.am
|
||||
+++ b/pcs_test/Makefile.am
|
||||
@@ -35,10 +35,15 @@ EXTRA_DIST = \
|
||||
resources/fenced_metadata.xml \
|
||||
resources/schedulerd_metadata.xml \
|
||||
resources/pcmk_api_rng/api-result.rng \
|
||||
- resources/pcmk_api_rng/crm_mon-2.4.rng \
|
||||
+ resources/pcmk_api_rng/crm_mon-2.29.rng \
|
||||
resources/pcmk_api_rng/digests-2.9.rng \
|
||||
- resources/pcmk_api_rng/fence-event-2.0.rng \
|
||||
- resources/pcmk_api_rng/resources-2.4.rng \
|
||||
+ resources/pcmk_api_rng/failure-2.8.rng \
|
||||
+ resources/pcmk_api_rng/fence-event-2.15.rng \
|
||||
+ resources/pcmk_api_rng/node-attrs-2.8.rng \
|
||||
+ resources/pcmk_api_rng/node-history-2.12.rng \
|
||||
+ resources/pcmk_api_rng/nodes-2.29.rng \
|
||||
+ resources/pcmk_api_rng/pacemakerd-health-2.25.rng \
|
||||
+ resources/pcmk_api_rng/resources-2.29.rng \
|
||||
resources/pcmk_api_rng/status-2.0.rng \
|
||||
resources/resource_agent_ocf_heartbeat_dummy_insane_action.xml \
|
||||
resources/resource_agent_ocf_heartbeat_dummy_utf8.xml \
|
||||
diff --git a/pcs_test/resources/crm_mon.minimal.xml b/pcs_test/resources/crm_mon.minimal.xml
|
||||
index 2c3473c0..df02fe3e 100644
|
||||
--- a/pcs_test/resources/crm_mon.minimal.xml
|
||||
+++ b/pcs_test/resources/crm_mon.minimal.xml
|
||||
@@ -1,4 +1,4 @@
|
||||
-<pacemaker-result api-version="2.3" request="crm_mon --output-as xml">
|
||||
+<pacemaker-result api-version="2.30" request="crm_mon --output-as xml">
|
||||
<summary>
|
||||
<stack type="corosync" />
|
||||
<current_dc present="false" />
|
||||
@@ -6,7 +6,7 @@
|
||||
<last_change time="Wed Nov 6 10:42:54 2019" user="hacluster" client="crmd" origin="node1" />
|
||||
<nodes_configured number="0" />
|
||||
<resources_configured number="0" disabled="0" blocked="0" />
|
||||
- <cluster_options stonith-enabled="true" symmetric-cluster="true" no-quorum-policy="stop" maintenance-mode="false" stop-all-resources="false" />
|
||||
+ <cluster_options stonith-enabled="true" symmetric-cluster="true" no-quorum-policy="stop" maintenance-mode="false" stop-all-resources="false" stonith-timeout-ms="60000" priority-fencing-delay-ms="0"/>
|
||||
</summary>
|
||||
<nodes />
|
||||
<resources />
|
||||
diff --git a/pcs_test/resources/pcmk_api_rng/api-result.rng b/pcs_test/resources/pcmk_api_rng/api-result.rng
|
||||
index fcd812ac..e01cfdcd 100644
|
||||
--- a/pcs_test/resources/pcmk_api_rng/api-result.rng
|
||||
+++ b/pcs_test/resources/pcmk_api_rng/api-result.rng
|
||||
@@ -6,7 +6,7 @@
|
||||
<attribute name="request"> <text /> </attribute>
|
||||
<optional>
|
||||
<choice>
|
||||
- <externalRef href="crm_mon-2.4.rng"/>
|
||||
+ <externalRef href="crm_mon-2.29.rng"/>
|
||||
<externalRef href="digests-2.9.rng"/>
|
||||
</choice>
|
||||
</optional>
|
||||
diff --git a/pcs_test/resources/pcmk_api_rng/crm_mon-2.29.rng b/pcs_test/resources/pcmk_api_rng/crm_mon-2.29.rng
|
||||
new file mode 100644
|
||||
index 00000000..9cc554cf
|
||||
--- /dev/null
|
||||
+++ b/pcs_test/resources/pcmk_api_rng/crm_mon-2.29.rng
|
||||
@@ -0,0 +1,213 @@
|
||||
+<?xml version="1.0" encoding="UTF-8"?>
|
||||
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
|
||||
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
|
||||
+
|
||||
+ <start>
|
||||
+ <ref name="element-crm-mon"/>
|
||||
+ </start>
|
||||
+
|
||||
+ <define name="element-crm-mon">
|
||||
+ <choice>
|
||||
+ <ref name="element-crm-mon-disconnected" />
|
||||
+ <group>
|
||||
+ <optional>
|
||||
+ <externalRef href="pacemakerd-health-2.25.rng" />
|
||||
+ </optional>
|
||||
+ <optional>
|
||||
+ <ref name="element-summary" />
|
||||
+ </optional>
|
||||
+ <optional>
|
||||
+ <ref name="nodes-list" />
|
||||
+ </optional>
|
||||
+ <optional>
|
||||
+ <ref name="resources-list" />
|
||||
+ </optional>
|
||||
+ <optional>
|
||||
+ <ref name="node-attributes-list" />
|
||||
+ </optional>
|
||||
+ <optional>
|
||||
+ <externalRef href="node-history-2.12.rng"/>
|
||||
+ </optional>
|
||||
+ <optional>
|
||||
+ <ref name="failures-list" />
|
||||
+ </optional>
|
||||
+ <optional>
|
||||
+ <ref name="fence-event-list" />
|
||||
+ </optional>
|
||||
+ <optional>
|
||||
+ <ref name="tickets-list" />
|
||||
+ </optional>
|
||||
+ <optional>
|
||||
+ <ref name="bans-list" />
|
||||
+ </optional>
|
||||
+ </group>
|
||||
+ </choice>
|
||||
+ </define>
|
||||
+
|
||||
+ <define name="element-crm-mon-disconnected">
|
||||
+ <element name="crm-mon-disconnected">
|
||||
+ <optional>
|
||||
+ <attribute name="description"> <text /> </attribute>
|
||||
+ </optional>
|
||||
+ <optional>
|
||||
+ <attribute name="pacemakerd-state"> <text /> </attribute>
|
||||
+ </optional>
|
||||
+ </element>
|
||||
+ </define>
|
||||
+
|
||||
+ <define name="element-summary">
|
||||
+ <element name="summary">
|
||||
+ <optional>
|
||||
+ <element name="stack">
|
||||
+ <attribute name="type"> <text /> </attribute>
|
||||
+ <optional>
|
||||
+ <attribute name="pacemakerd-state">
|
||||
+ <text />
|
||||
+ </attribute>
|
||||
+ </optional>
|
||||
+ </element>
|
||||
+ </optional>
|
||||
+ <optional>
|
||||
+ <element name="current_dc">
|
||||
+ <attribute name="present"> <data type="boolean" /> </attribute>
|
||||
+ <optional>
|
||||
+ <group>
|
||||
+ <attribute name="version"> <text /> </attribute>
|
||||
+ <attribute name="name"> <text /> </attribute>
|
||||
+ <attribute name="id"> <text /> </attribute>
|
||||
+ <attribute name="with_quorum"> <data type="boolean" /> </attribute>
|
||||
+ </group>
|
||||
+ </optional>
|
||||
+ <optional>
|
||||
+ <attribute name="mixed_version"> <data type="boolean" /> </attribute>
|
||||
+ </optional>
|
||||
+ </element>
|
||||
+ </optional>
|
||||
+ <optional>
|
||||
+ <element name="last_update">
|
||||
+ <attribute name="time"> <text /> </attribute>
|
||||
+ <optional>
|
||||
+ <attribute name="origin"> <text /> </attribute>
|
||||
+ </optional>
|
||||
+ </element>
|
||||
+ <element name="last_change">
|
||||
+ <attribute name="time"> <text /> </attribute>
|
||||
+ <attribute name="user"> <text /> </attribute>
|
||||
+ <attribute name="client"> <text /> </attribute>
|
||||
+ <attribute name="origin"> <text /> </attribute>
|
||||
+ </element>
|
||||
+ </optional>
|
||||
+ <optional>
|
||||
+ <element name="nodes_configured">
|
||||
+ <attribute name="number"> <data type="nonNegativeInteger" /> </attribute>
|
||||
+ </element>
|
||||
+ <element name="resources_configured">
|
||||
+ <attribute name="number"> <data type="nonNegativeInteger" /> </attribute>
|
||||
+ <attribute name="disabled"> <data type="nonNegativeInteger" /> </attribute>
|
||||
+ <attribute name="blocked"> <data type="nonNegativeInteger" /> </attribute>
|
||||
+ </element>
|
||||
+ </optional>
|
||||
+ <optional>
|
||||
+ <element name="cluster_options">
|
||||
+ <attribute name="stonith-enabled"> <data type="boolean" /> </attribute>
|
||||
+ <attribute name="symmetric-cluster"> <data type="boolean" /> </attribute>
|
||||
+ <attribute name="no-quorum-policy"> <text /> </attribute>
|
||||
+ <attribute name="maintenance-mode"> <data type="boolean" /> </attribute>
|
||||
+ <attribute name="stop-all-resources"> <data type="boolean" /> </attribute>
|
||||
+ <attribute name="stonith-timeout-ms"> <data type="integer" /> </attribute>
|
||||
+ <attribute name="priority-fencing-delay-ms"> <data type="integer" /> </attribute>
|
||||
+ </element>
|
||||
+ </optional>
|
||||
+ </element>
|
||||
+ </define>
|
||||
+
|
||||
+ <define name="resources-list">
|
||||
+ <element name="resources">
|
||||
+ <zeroOrMore>
|
||||
+ <externalRef href="resources-2.29.rng" />
|
||||
+ </zeroOrMore>
|
||||
+ </element>
|
||||
+ </define>
|
||||
+
|
||||
+ <define name="nodes-list">
|
||||
+ <element name="nodes">
|
||||
+ <zeroOrMore>
|
||||
+ <externalRef href="nodes-2.29.rng" />
|
||||
+ </zeroOrMore>
|
||||
+ </element>
|
||||
+ </define>
|
||||
+
|
||||
+ <define name="node-attributes-list">
|
||||
+ <element name="node_attributes">
|
||||
+ <zeroOrMore>
|
||||
+ <externalRef href="node-attrs-2.8.rng" />
|
||||
+ </zeroOrMore>
|
||||
+ </element>
|
||||
+ </define>
|
||||
+
|
||||
+ <define name="failures-list">
|
||||
+ <element name="failures">
|
||||
+ <zeroOrMore>
|
||||
+ <externalRef href="failure-2.8.rng" />
|
||||
+ </zeroOrMore>
|
||||
+ </element>
|
||||
+ </define>
|
||||
+
|
||||
+ <define name="fence-event-list">
|
||||
+ <element name="fence_history">
|
||||
+ <optional>
|
||||
+ <attribute name="status"> <data type="integer" /> </attribute>
|
||||
+ </optional>
|
||||
+ <zeroOrMore>
|
||||
+ <externalRef href="fence-event-2.15.rng" />
|
||||
+ </zeroOrMore>
|
||||
+ </element>
|
||||
+ </define>
|
||||
+
|
||||
+ <define name="tickets-list">
|
||||
+ <element name="tickets">
|
||||
+ <zeroOrMore>
|
||||
+ <ref name="element-ticket" />
|
||||
+ </zeroOrMore>
|
||||
+ </element>
|
||||
+ </define>
|
||||
+
|
||||
+ <define name="bans-list">
|
||||
+ <element name="bans">
|
||||
+ <zeroOrMore>
|
||||
+ <ref name="element-ban" />
|
||||
+ </zeroOrMore>
|
||||
+ </element>
|
||||
+ </define>
|
||||
+
|
||||
+ <define name="element-ticket">
|
||||
+ <element name="ticket">
|
||||
+ <attribute name="id"> <text /> </attribute>
|
||||
+ <attribute name="status">
|
||||
+ <choice>
|
||||
+ <value>granted</value>
|
||||
+ <value>revoked</value>
|
||||
+ </choice>
|
||||
+ </attribute>
|
||||
+ <attribute name="standby"> <data type="boolean" /> </attribute>
|
||||
+ <optional>
|
||||
+ <attribute name="last-granted"> <text /> </attribute>
|
||||
+ </optional>
|
||||
+ </element>
|
||||
+ </define>
|
||||
+
|
||||
+ <define name="element-ban">
|
||||
+ <element name="ban">
|
||||
+ <attribute name="id"> <text /> </attribute>
|
||||
+ <attribute name="resource"> <text /> </attribute>
|
||||
+ <attribute name="node"> <text /> </attribute>
|
||||
+ <attribute name="weight"> <data type="integer" /> </attribute>
|
||||
+ <attribute name="promoted-only"> <data type="boolean" /> </attribute>
|
||||
+ <!-- DEPRECATED: master_only is a duplicate of promoted-only that is
|
||||
+ provided solely for API backward compatibility. It will be
|
||||
+ removed in a future release. Check promoted-only instead.
|
||||
+ -->
|
||||
+ <attribute name="master_only"> <data type="boolean" /> </attribute>
|
||||
+ </element>
|
||||
+ </define>
|
||||
+</grammar>
|
||||
diff --git a/pcs_test/resources/pcmk_api_rng/crm_mon-2.4.rng b/pcs_test/resources/pcmk_api_rng/crm_mon-2.4.rng
|
||||
deleted file mode 100644
|
||||
index 88973a4e..00000000
|
||||
--- a/pcs_test/resources/pcmk_api_rng/crm_mon-2.4.rng
|
||||
+++ /dev/null
|
||||
@@ -1,311 +0,0 @@
|
||||
-<?xml version="1.0" encoding="UTF-8"?>
|
||||
-<grammar xmlns="http://relaxng.org/ns/structure/1.0"
|
||||
- datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
|
||||
-
|
||||
- <start>
|
||||
- <ref name="element-crm-mon"/>
|
||||
- </start>
|
||||
-
|
||||
- <define name="element-crm-mon">
|
||||
- <optional>
|
||||
- <ref name="element-summary" />
|
||||
- </optional>
|
||||
- <optional>
|
||||
- <ref name="nodes-list" />
|
||||
- </optional>
|
||||
- <optional>
|
||||
- <ref name="resources-list" />
|
||||
- </optional>
|
||||
- <optional>
|
||||
- <ref name="node-attributes-list" />
|
||||
- </optional>
|
||||
- <optional>
|
||||
- <ref name="node-history-list" />
|
||||
- </optional>
|
||||
- <optional>
|
||||
- <ref name="failures-list" />
|
||||
- </optional>
|
||||
- <optional>
|
||||
- <ref name="fence-event-list" />
|
||||
- </optional>
|
||||
- <optional>
|
||||
- <ref name="tickets-list" />
|
||||
- </optional>
|
||||
- <optional>
|
||||
- <ref name="bans-list" />
|
||||
- </optional>
|
||||
- </define>
|
||||
-
|
||||
- <define name="element-summary">
|
||||
- <element name="summary">
|
||||
- <optional>
|
||||
- <element name="stack">
|
||||
- <attribute name="type"> <text /> </attribute>
|
||||
- </element>
|
||||
- </optional>
|
||||
- <optional>
|
||||
- <element name="current_dc">
|
||||
- <attribute name="present"> <data type="boolean" /> </attribute>
|
||||
- <optional>
|
||||
- <group>
|
||||
- <attribute name="version"> <text /> </attribute>
|
||||
- <attribute name="name"> <text /> </attribute>
|
||||
- <attribute name="id"> <text /> </attribute>
|
||||
- <attribute name="with_quorum"> <data type="boolean" /> </attribute>
|
||||
- </group>
|
||||
- </optional>
|
||||
- </element>
|
||||
- </optional>
|
||||
- <optional>
|
||||
- <element name="last_update">
|
||||
- <attribute name="time"> <text /> </attribute>
|
||||
- </element>
|
||||
- <element name="last_change">
|
||||
- <attribute name="time"> <text /> </attribute>
|
||||
- <attribute name="user"> <text /> </attribute>
|
||||
- <attribute name="client"> <text /> </attribute>
|
||||
- <attribute name="origin"> <text /> </attribute>
|
||||
- </element>
|
||||
- </optional>
|
||||
- <optional>
|
||||
- <element name="nodes_configured">
|
||||
- <attribute name="number"> <data type="nonNegativeInteger" /> </attribute>
|
||||
- </element>
|
||||
- <element name="resources_configured">
|
||||
- <attribute name="number"> <data type="nonNegativeInteger" /> </attribute>
|
||||
- <attribute name="disabled"> <data type="nonNegativeInteger" /> </attribute>
|
||||
- <attribute name="blocked"> <data type="nonNegativeInteger" /> </attribute>
|
||||
- </element>
|
||||
- </optional>
|
||||
- <optional>
|
||||
- <element name="cluster_options">
|
||||
- <attribute name="stonith-enabled"> <data type="boolean" /> </attribute>
|
||||
- <attribute name="symmetric-cluster"> <data type="boolean" /> </attribute>
|
||||
- <attribute name="no-quorum-policy"> <text /> </attribute>
|
||||
- <attribute name="maintenance-mode"> <data type="boolean" /> </attribute>
|
||||
- <attribute name="stop-all-resources"> <data type="boolean" /> </attribute>
|
||||
- </element>
|
||||
- </optional>
|
||||
- </element>
|
||||
- </define>
|
||||
-
|
||||
- <define name="resources-list">
|
||||
- <element name="resources">
|
||||
- <zeroOrMore>
|
||||
- <externalRef href="resources-2.4.rng" />
|
||||
- </zeroOrMore>
|
||||
- </element>
|
||||
- </define>
|
||||
-
|
||||
- <define name="nodes-list">
|
||||
- <element name="nodes">
|
||||
- <zeroOrMore>
|
||||
- <ref name="element-full-node" />
|
||||
- </zeroOrMore>
|
||||
- </element>
|
||||
- </define>
|
||||
-
|
||||
- <define name="node-attributes-list">
|
||||
- <element name="node_attributes">
|
||||
- <zeroOrMore>
|
||||
- <ref name="element-node-with-attributes" />
|
||||
- </zeroOrMore>
|
||||
- </element>
|
||||
- </define>
|
||||
-
|
||||
- <define name="node-history-list">
|
||||
- <element name="node_history">
|
||||
- <zeroOrMore>
|
||||
- <ref name="element-node-history" />
|
||||
- </zeroOrMore>
|
||||
- </element>
|
||||
- </define>
|
||||
-
|
||||
- <define name="failures-list">
|
||||
- <element name="failures">
|
||||
- <zeroOrMore>
|
||||
- <ref name="element-failure" />
|
||||
- </zeroOrMore>
|
||||
- </element>
|
||||
- </define>
|
||||
-
|
||||
- <define name="fence-event-list">
|
||||
- <element name="fence_history">
|
||||
- <optional>
|
||||
- <attribute name="status"> <data type="integer" /> </attribute>
|
||||
- </optional>
|
||||
- <zeroOrMore>
|
||||
- <externalRef href="fence-event-2.0.rng" />
|
||||
- </zeroOrMore>
|
||||
- </element>
|
||||
- </define>
|
||||
-
|
||||
- <define name="tickets-list">
|
||||
- <element name="tickets">
|
||||
- <zeroOrMore>
|
||||
- <ref name="element-ticket" />
|
||||
- </zeroOrMore>
|
||||
- </element>
|
||||
- </define>
|
||||
-
|
||||
- <define name="bans-list">
|
||||
- <element name="bans">
|
||||
- <zeroOrMore>
|
||||
- <ref name="element-ban" />
|
||||
- </zeroOrMore>
|
||||
- </element>
|
||||
- </define>
|
||||
-
|
||||
- <define name="element-full-node">
|
||||
- <element name="node">
|
||||
- <attribute name="name"> <text/> </attribute>
|
||||
- <attribute name="id"> <text/> </attribute>
|
||||
- <attribute name="online"> <data type="boolean" /> </attribute>
|
||||
- <attribute name="standby"> <data type="boolean" /> </attribute>
|
||||
- <attribute name="standby_onfail"> <data type="boolean" /> </attribute>
|
||||
- <attribute name="maintenance"> <data type="boolean" /> </attribute>
|
||||
- <attribute name="pending"> <data type="boolean" /> </attribute>
|
||||
- <attribute name="unclean"> <data type="boolean" /> </attribute>
|
||||
- <attribute name="shutdown"> <data type="boolean" /> </attribute>
|
||||
- <attribute name="expected_up"> <data type="boolean" /> </attribute>
|
||||
- <attribute name="is_dc"> <data type="boolean" /> </attribute>
|
||||
- <attribute name="resources_running"> <data type="nonNegativeInteger" /> </attribute>
|
||||
- <attribute name="type">
|
||||
- <choice>
|
||||
- <value>unknown</value>
|
||||
- <value>member</value>
|
||||
- <value>remote</value>
|
||||
- <value>ping</value>
|
||||
- </choice>
|
||||
- </attribute>
|
||||
- <optional>
|
||||
- <!-- for virtualized pacemaker_remote nodes, crm_mon 1.1.13 uses
|
||||
- "container_id" while later versions use "id_as_resource" -->
|
||||
- <choice>
|
||||
- <attribute name="container_id"> <text/> </attribute>
|
||||
- <attribute name="id_as_resource"> <text/> </attribute>
|
||||
- </choice>
|
||||
- </optional>
|
||||
- <externalRef href="resources-2.4.rng" />
|
||||
- </element>
|
||||
- </define>
|
||||
-
|
||||
- <define name="element-node-with-attributes">
|
||||
- <element name="node">
|
||||
- <attribute name="name"> <text /> </attribute>
|
||||
- <zeroOrMore>
|
||||
- <element name="attribute">
|
||||
- <attribute name="name"> <text /> </attribute>
|
||||
- <attribute name="value"> <text /> </attribute>
|
||||
- <optional>
|
||||
- <attribute name="expected"> <data type="nonNegativeInteger" /> </attribute>
|
||||
- </optional>
|
||||
- </element>
|
||||
- </zeroOrMore>
|
||||
- </element>
|
||||
- </define>
|
||||
-
|
||||
- <define name="element-node-history">
|
||||
- <element name="node">
|
||||
- <attribute name="name"> <text /> </attribute>
|
||||
- <zeroOrMore>
|
||||
- <ref name="element-resource-history" />
|
||||
- </zeroOrMore>
|
||||
- </element>
|
||||
- </define>
|
||||
-
|
||||
- <define name="element-resource-history">
|
||||
- <element name="resource_history">
|
||||
- <attribute name="id"> <text /> </attribute>
|
||||
- <attribute name="orphan"> <data type="boolean" /> </attribute>
|
||||
- <optional>
|
||||
- <group>
|
||||
- <attribute name="migration-threshold"> <data type="nonNegativeInteger" /> </attribute>
|
||||
- <optional>
|
||||
- <attribute name="fail-count"> <text /> </attribute>
|
||||
- </optional>
|
||||
- <optional>
|
||||
- <attribute name="last-failure"> <text /> </attribute>
|
||||
- </optional>
|
||||
- </group>
|
||||
- </optional>
|
||||
- <zeroOrMore>
|
||||
- <ref name="element-operation-history" />
|
||||
- </zeroOrMore>
|
||||
- </element>
|
||||
- </define>
|
||||
-
|
||||
- <define name="element-operation-history">
|
||||
- <element name="operation_history">
|
||||
- <attribute name="call"> <text /> </attribute>
|
||||
- <attribute name="task"> <text /> </attribute>
|
||||
- <optional>
|
||||
- <attribute name="interval"> <text /> </attribute>
|
||||
- </optional>
|
||||
- <optional>
|
||||
- <attribute name="last-rc-change"> <text /> </attribute>
|
||||
- </optional>
|
||||
- <optional>
|
||||
- <attribute name="last-run"> <text /> </attribute>
|
||||
- </optional>
|
||||
- <optional>
|
||||
- <attribute name="exec-time"> <text /> </attribute>
|
||||
- </optional>
|
||||
- <optional>
|
||||
- <attribute name="queue-time"> <text /> </attribute>
|
||||
- </optional>
|
||||
- <attribute name="rc"> <data type="integer" /> </attribute>
|
||||
- <attribute name="rc_text"> <text /> </attribute>
|
||||
- </element>
|
||||
- </define>
|
||||
-
|
||||
- <define name="element-failure">
|
||||
- <element name="failure">
|
||||
- <choice>
|
||||
- <attribute name="op_key"> <text/> </attribute>
|
||||
- <attribute name="id"> <text/> </attribute>
|
||||
- </choice>
|
||||
- <attribute name="node"> <text /> </attribute>
|
||||
- <attribute name="exitstatus"> <text /> </attribute>
|
||||
- <attribute name="exitreason"> <text /> </attribute>
|
||||
- <attribute name="exitcode"> <data type="nonNegativeInteger" /> </attribute>
|
||||
- <attribute name="call"> <data type="nonNegativeInteger" /> </attribute>
|
||||
- <attribute name="status"> <text /> </attribute>
|
||||
- <optional>
|
||||
- <group>
|
||||
- <attribute name="last-rc-change"> <text /> </attribute>
|
||||
- <attribute name="queued"> <data type="nonNegativeInteger" /> </attribute>
|
||||
- <attribute name="exec"> <data type="nonNegativeInteger" /> </attribute>
|
||||
- <attribute name="interval"> <data type="nonNegativeInteger" /> </attribute>
|
||||
- <attribute name="task"> <text /> </attribute>
|
||||
- </group>
|
||||
- </optional>
|
||||
- </element>
|
||||
- </define>
|
||||
-
|
||||
- <define name="element-ticket">
|
||||
- <element name="ticket">
|
||||
- <attribute name="id"> <text /> </attribute>
|
||||
- <attribute name="status">
|
||||
- <choice>
|
||||
- <value>granted</value>
|
||||
- <value>revoked</value>
|
||||
- </choice>
|
||||
- </attribute>
|
||||
- <attribute name="standby"> <data type="boolean" /> </attribute>
|
||||
- <optional>
|
||||
- <attribute name="last-granted"> <text /> </attribute>
|
||||
- </optional>
|
||||
- </element>
|
||||
- </define>
|
||||
-
|
||||
- <define name="element-ban">
|
||||
- <element name="ban">
|
||||
- <attribute name="id"> <text /> </attribute>
|
||||
- <attribute name="resource"> <text /> </attribute>
|
||||
- <attribute name="node"> <text /> </attribute>
|
||||
- <attribute name="weight"> <data type="integer" /> </attribute>
|
||||
- <attribute name="master_only"> <data type="boolean" /> </attribute>
|
||||
- </element>
|
||||
- </define>
|
||||
-</grammar>
|
||||
diff --git a/pcs_test/resources/pcmk_api_rng/failure-2.8.rng b/pcs_test/resources/pcmk_api_rng/failure-2.8.rng
|
||||
new file mode 100644
|
||||
index 00000000..a36d2ea9
|
||||
--- /dev/null
|
||||
+++ b/pcs_test/resources/pcmk_api_rng/failure-2.8.rng
|
||||
@@ -0,0 +1,33 @@
|
||||
+<?xml version="1.0" encoding="UTF-8"?>
|
||||
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
|
||||
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
|
||||
+
|
||||
+ <start>
|
||||
+ <ref name="element-failure"/>
|
||||
+ </start>
|
||||
+
|
||||
+ <define name="element-failure">
|
||||
+ <element name="failure">
|
||||
+ <choice>
|
||||
+ <attribute name="op_key"> <text/> </attribute>
|
||||
+ <attribute name="id"> <text/> </attribute>
|
||||
+ </choice>
|
||||
+ <attribute name="node"> <text /> </attribute>
|
||||
+ <attribute name="exitstatus"> <text /> </attribute>
|
||||
+ <attribute name="exitreason"> <text /> </attribute>
|
||||
+ <attribute name="exitcode"> <data type="nonNegativeInteger" /> </attribute>
|
||||
+ <attribute name="call"> <data type="integer" /> </attribute>
|
||||
+ <attribute name="status"> <text /> </attribute>
|
||||
+ <optional>
|
||||
+ <group>
|
||||
+ <attribute name="last-rc-change"> <text /> </attribute>
|
||||
+ <attribute name="queued"> <data type="nonNegativeInteger" /> </attribute>
|
||||
+ <attribute name="exec"> <data type="nonNegativeInteger" /> </attribute>
|
||||
+ <attribute name="interval"> <data type="nonNegativeInteger" /> </attribute>
|
||||
+ <attribute name="task"> <text /> </attribute>
|
||||
+ </group>
|
||||
+ </optional>
|
||||
+ </element>
|
||||
+ </define>
|
||||
+</grammar>
|
||||
+
|
||||
diff --git a/pcs_test/resources/pcmk_api_rng/fence-event-2.0.rng b/pcs_test/resources/pcmk_api_rng/fence-event-2.15.rng
|
||||
similarity index 91%
|
||||
rename from pcs_test/resources/pcmk_api_rng/fence-event-2.0.rng
|
||||
rename to pcs_test/resources/pcmk_api_rng/fence-event-2.15.rng
|
||||
index e54687cd..8e000caf 100644
|
||||
--- a/pcs_test/resources/pcmk_api_rng/fence-event-2.0.rng
|
||||
+++ b/pcs_test/resources/pcmk_api_rng/fence-event-2.15.rng
|
||||
@@ -18,6 +18,9 @@
|
||||
<optional>
|
||||
<attribute name="extended-status"> <text /> </attribute>
|
||||
</optional>
|
||||
+ <optional>
|
||||
+ <attribute name="exit-reason"> <text /> </attribute>
|
||||
+ </optional>
|
||||
<optional>
|
||||
<attribute name="delegate"> <text /> </attribute>
|
||||
</optional>
|
||||
diff --git a/pcs_test/resources/pcmk_api_rng/node-attrs-2.8.rng b/pcs_test/resources/pcmk_api_rng/node-attrs-2.8.rng
|
||||
new file mode 100644
|
||||
index 00000000..754ddb9e
|
||||
--- /dev/null
|
||||
+++ b/pcs_test/resources/pcmk_api_rng/node-attrs-2.8.rng
|
||||
@@ -0,0 +1,24 @@
|
||||
+<?xml version="1.0" encoding="UTF-8"?>
|
||||
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
|
||||
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
|
||||
+
|
||||
+ <start>
|
||||
+ <ref name="element-node-with-attributes"/>
|
||||
+ </start>
|
||||
+
|
||||
+ <define name="element-node-with-attributes">
|
||||
+ <element name="node">
|
||||
+ <attribute name="name"> <text /> </attribute>
|
||||
+ <zeroOrMore>
|
||||
+ <element name="attribute">
|
||||
+ <attribute name="name"> <text /> </attribute>
|
||||
+ <attribute name="value"> <text /> </attribute>
|
||||
+ <optional>
|
||||
+ <attribute name="expected"> <data type="integer" /> </attribute>
|
||||
+ </optional>
|
||||
+ </element>
|
||||
+ </zeroOrMore>
|
||||
+ </element>
|
||||
+ </define>
|
||||
+
|
||||
+</grammar>
|
||||
diff --git a/pcs_test/resources/pcmk_api_rng/node-history-2.12.rng b/pcs_test/resources/pcmk_api_rng/node-history-2.12.rng
|
||||
new file mode 100644
|
||||
index 00000000..9628000e
|
||||
--- /dev/null
|
||||
+++ b/pcs_test/resources/pcmk_api_rng/node-history-2.12.rng
|
||||
@@ -0,0 +1,70 @@
|
||||
+<?xml version="1.0" encoding="UTF-8"?>
|
||||
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
|
||||
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
|
||||
+
|
||||
+ <start>
|
||||
+ <ref name="node-history-list" />
|
||||
+ </start>
|
||||
+
|
||||
+ <define name="node-history-list">
|
||||
+ <element name="node_history">
|
||||
+ <zeroOrMore>
|
||||
+ <ref name="element-node-history" />
|
||||
+ </zeroOrMore>
|
||||
+ </element>
|
||||
+ </define>
|
||||
+
|
||||
+ <define name="element-node-history">
|
||||
+ <element name="node">
|
||||
+ <attribute name="name"> <text /> </attribute>
|
||||
+ <zeroOrMore>
|
||||
+ <ref name="element-resource-history" />
|
||||
+ </zeroOrMore>
|
||||
+ </element>
|
||||
+ </define>
|
||||
+
|
||||
+ <define name="element-resource-history">
|
||||
+ <element name="resource_history">
|
||||
+ <attribute name="id"> <text /> </attribute>
|
||||
+ <attribute name="orphan"> <data type="boolean" /> </attribute>
|
||||
+ <optional>
|
||||
+ <group>
|
||||
+ <attribute name="migration-threshold"> <data type="nonNegativeInteger" /> </attribute>
|
||||
+ <optional>
|
||||
+ <attribute name="fail-count"> <text /> </attribute>
|
||||
+ </optional>
|
||||
+ <optional>
|
||||
+ <attribute name="last-failure"> <text /> </attribute>
|
||||
+ </optional>
|
||||
+ </group>
|
||||
+ </optional>
|
||||
+ <zeroOrMore>
|
||||
+ <ref name="element-operation-history" />
|
||||
+ </zeroOrMore>
|
||||
+ </element>
|
||||
+ </define>
|
||||
+
|
||||
+ <define name="element-operation-history">
|
||||
+ <element name="operation_history">
|
||||
+ <attribute name="call"> <text /> </attribute>
|
||||
+ <attribute name="task"> <text /> </attribute>
|
||||
+ <optional>
|
||||
+ <attribute name="interval"> <text /> </attribute>
|
||||
+ </optional>
|
||||
+ <optional>
|
||||
+ <attribute name="last-rc-change"> <text /> </attribute>
|
||||
+ </optional>
|
||||
+ <optional>
|
||||
+ <attribute name="last-run"> <text /> </attribute>
|
||||
+ </optional>
|
||||
+ <optional>
|
||||
+ <attribute name="exec-time"> <text /> </attribute>
|
||||
+ </optional>
|
||||
+ <optional>
|
||||
+ <attribute name="queue-time"> <text /> </attribute>
|
||||
+ </optional>
|
||||
+ <attribute name="rc"> <data type="integer" /> </attribute>
|
||||
+ <attribute name="rc_text"> <text /> </attribute>
|
||||
+ </element>
|
||||
+ </define>
|
||||
+</grammar>
|
||||
diff --git a/pcs_test/resources/pcmk_api_rng/nodes-2.29.rng b/pcs_test/resources/pcmk_api_rng/nodes-2.29.rng
|
||||
new file mode 100644
|
||||
index 00000000..7dd17989
|
||||
--- /dev/null
|
||||
+++ b/pcs_test/resources/pcmk_api_rng/nodes-2.29.rng
|
||||
@@ -0,0 +1,54 @@
|
||||
+<?xml version="1.0" encoding="UTF-8"?>
|
||||
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
|
||||
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
|
||||
+
|
||||
+ <start>
|
||||
+ <ref name="element-full-node"/>
|
||||
+ </start>
|
||||
+
|
||||
+ <define name="element-full-node">
|
||||
+ <element name="node">
|
||||
+ <attribute name="name"> <text/> </attribute>
|
||||
+ <attribute name="id"> <text/> </attribute>
|
||||
+ <attribute name="online"> <data type="boolean" /> </attribute>
|
||||
+ <attribute name="standby"> <data type="boolean" /> </attribute>
|
||||
+ <attribute name="standby_onfail"> <data type="boolean" /> </attribute>
|
||||
+ <attribute name="maintenance"> <data type="boolean" /> </attribute>
|
||||
+ <attribute name="pending"> <data type="boolean" /> </attribute>
|
||||
+ <attribute name="unclean"> <data type="boolean" /> </attribute>
|
||||
+ <optional>
|
||||
+ <attribute name="health">
|
||||
+ <choice>
|
||||
+ <value>red</value>
|
||||
+ <value>yellow</value>
|
||||
+ <value>green</value>
|
||||
+ </choice>
|
||||
+ </attribute>
|
||||
+ </optional>
|
||||
+ <optional>
|
||||
+ <attribute name="feature_set"> <text/> </attribute>
|
||||
+ </optional>
|
||||
+ <attribute name="shutdown"> <data type="boolean" /> </attribute>
|
||||
+ <attribute name="expected_up"> <data type="boolean" /> </attribute>
|
||||
+ <attribute name="is_dc"> <data type="boolean" /> </attribute>
|
||||
+ <attribute name="resources_running"> <data type="nonNegativeInteger" /> </attribute>
|
||||
+ <attribute name="type">
|
||||
+ <choice>
|
||||
+ <value>unknown</value>
|
||||
+ <value>member</value>
|
||||
+ <value>remote</value>
|
||||
+ <value>ping</value>
|
||||
+ </choice>
|
||||
+ </attribute>
|
||||
+ <optional>
|
||||
+ <!-- for virtualized pacemaker_remote nodes, crm_mon 1.1.13 uses
|
||||
+ "container_id" while later versions use "id_as_resource" -->
|
||||
+ <choice>
|
||||
+ <attribute name="container_id"> <text/> </attribute>
|
||||
+ <attribute name="id_as_resource"> <text/> </attribute>
|
||||
+ </choice>
|
||||
+ </optional>
|
||||
+ <externalRef href="resources-2.29.rng" />
|
||||
+ </element>
|
||||
+ </define>
|
||||
+</grammar>
|
||||
diff --git a/pcs_test/resources/pcmk_api_rng/pacemakerd-health-2.25.rng b/pcs_test/resources/pcmk_api_rng/pacemakerd-health-2.25.rng
|
||||
new file mode 100644
|
||||
index 00000000..2089b25f
|
||||
--- /dev/null
|
||||
+++ b/pcs_test/resources/pcmk_api_rng/pacemakerd-health-2.25.rng
|
||||
@@ -0,0 +1,20 @@
|
||||
+<?xml version="1.0" encoding="UTF-8"?>
|
||||
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
|
||||
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
|
||||
+
|
||||
+ <start>
|
||||
+ <ref name="element-pacemakerd-health"/>
|
||||
+ </start>
|
||||
+
|
||||
+ <define name="element-pacemakerd-health">
|
||||
+ <element name="pacemakerd">
|
||||
+ <optional>
|
||||
+ <attribute name="sys_from"> <text /> </attribute>
|
||||
+ </optional>
|
||||
+ <attribute name="state"> <text /> </attribute>
|
||||
+ <optional>
|
||||
+ <attribute name="last_updated"> <text /> </attribute>
|
||||
+ </optional>
|
||||
+ </element>
|
||||
+ </define>
|
||||
+</grammar>
|
||||
diff --git a/pcs_test/resources/pcmk_api_rng/resources-2.4.rng b/pcs_test/resources/pcmk_api_rng/resources-2.29.rng
|
||||
similarity index 76%
|
||||
rename from pcs_test/resources/pcmk_api_rng/resources-2.4.rng
|
||||
rename to pcs_test/resources/pcmk_api_rng/resources-2.29.rng
|
||||
index e2795836..f4214a7c 100644
|
||||
--- a/pcs_test/resources/pcmk_api_rng/resources-2.4.rng
|
||||
+++ b/pcs_test/resources/pcmk_api_rng/resources-2.29.rng
|
||||
@@ -35,6 +35,16 @@
|
||||
</attribute>
|
||||
<attribute name="image"> <text/> </attribute>
|
||||
<attribute name="unique"> <data type="boolean" /> </attribute>
|
||||
+ <optional>
|
||||
+ <attribute name="maintenance">
|
||||
+ <data type="boolean" />
|
||||
+ </attribute>
|
||||
+ </optional>
|
||||
+ <optional>
|
||||
+ <attribute name="description">
|
||||
+ <text/>
|
||||
+ </attribute>
|
||||
+ </optional>
|
||||
<attribute name="managed"> <data type="boolean" /> </attribute>
|
||||
<attribute name="failed"> <data type="boolean" /> </attribute>
|
||||
<zeroOrMore>
|
||||
@@ -53,6 +63,16 @@
|
||||
<attribute name="id"> <text/> </attribute>
|
||||
<attribute name="multi_state"> <data type="boolean" /> </attribute>
|
||||
<attribute name="unique"> <data type="boolean" /> </attribute>
|
||||
+ <optional>
|
||||
+ <attribute name="maintenance">
|
||||
+ <data type="boolean" />
|
||||
+ </attribute>
|
||||
+ </optional>
|
||||
+ <optional>
|
||||
+ <attribute name="description">
|
||||
+ <text/>
|
||||
+ </attribute>
|
||||
+ </optional>
|
||||
<attribute name="managed"> <data type="boolean" /> </attribute>
|
||||
<attribute name="disabled"> <data type="boolean" /> </attribute>
|
||||
<attribute name="failed"> <data type="boolean" /> </attribute>
|
||||
@@ -68,6 +88,16 @@
|
||||
<element name="group">
|
||||
<attribute name="id"> <text/> </attribute>
|
||||
<attribute name="number_resources"> <data type="nonNegativeInteger" /> </attribute>
|
||||
+ <optional>
|
||||
+ <attribute name="maintenance">
|
||||
+ <data type="boolean" />
|
||||
+ </attribute>
|
||||
+ </optional>
|
||||
+ <optional>
|
||||
+ <attribute name="description">
|
||||
+ <text/>
|
||||
+ </attribute>
|
||||
+ </optional>
|
||||
<attribute name="managed"> <data type="boolean" /> </attribute>
|
||||
<attribute name="disabled"> <data type="boolean" /> </attribute>
|
||||
<ref name="element-resource-list" />
|
||||
@@ -87,13 +117,26 @@
|
||||
<optional>
|
||||
<attribute name="blocked"> <data type="boolean" /> </attribute>
|
||||
</optional>
|
||||
- <attribute name="managed"> <data type="boolean" /> </attribute>
|
||||
+ <optional>
|
||||
+ <attribute name="maintenance">
|
||||
+ <data type="boolean" />
|
||||
+ </attribute>
|
||||
+ </optional>
|
||||
+ <optional>
|
||||
+ <attribute name="description">
|
||||
+ <text/>
|
||||
+ </attribute>
|
||||
+ </optional>
|
||||
<attribute name="failed"> <data type="boolean" /> </attribute>
|
||||
+ <attribute name="managed"> <data type="boolean" /> </attribute>
|
||||
<attribute name="failure_ignored"> <data type="boolean" /> </attribute>
|
||||
<attribute name="nodes_running_on"> <data type="nonNegativeInteger" /> </attribute>
|
||||
<optional>
|
||||
<attribute name="pending"> <text/> </attribute>
|
||||
</optional>
|
||||
+ <optional>
|
||||
+ <attribute name="locked_to"> <text/> </attribute>
|
||||
+ </optional>
|
||||
<zeroOrMore>
|
||||
<element name="node">
|
||||
<attribute name="name"> <text/> </attribute>
|
||||
--
|
||||
2.25.1
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user