import pcs-0.10.12-6.el8
This commit is contained in:
parent
42cb490c07
commit
80bfaed5cb
6
.gitignore
vendored
6
.gitignore
vendored
@ -9,9 +9,9 @@ SOURCES/ffi-1.13.1.gem
|
||||
SOURCES/json-2.3.0.gem
|
||||
SOURCES/mustermann-1.1.1.gem
|
||||
SOURCES/open4-1.3.4-1.gem
|
||||
SOURCES/pcs-0.10.10.tar.gz
|
||||
SOURCES/pcs-web-ui-0.1.7.tar.gz
|
||||
SOURCES/pcs-web-ui-node-modules-0.1.7.tar.xz
|
||||
SOURCES/pcs-0.10.12.tar.gz
|
||||
SOURCES/pcs-web-ui-0.1.12.tar.gz
|
||||
SOURCES/pcs-web-ui-node-modules-0.1.12.tar.xz
|
||||
SOURCES/pyagentx-0.4.pcs.2.tar.gz
|
||||
SOURCES/python-dateutil-2.8.1.tar.gz
|
||||
SOURCES/rack-2.2.3.gem
|
||||
|
@ -9,9 +9,9 @@ cfa25e7a3760c3ec16723cb8263d9b7a52d0eadf SOURCES/ffi-1.13.1.gem
|
||||
0230e8c5a37f1543982e5b04be503dd5f9004b47 SOURCES/json-2.3.0.gem
|
||||
50a4e37904485810cb05e27d75c9783e5a8f3402 SOURCES/mustermann-1.1.1.gem
|
||||
41a7fe9f8e3e02da5ae76c821b89c5b376a97746 SOURCES/open4-1.3.4-1.gem
|
||||
a1c0585455b7e050c33598598a045ccd2776cb28 SOURCES/pcs-0.10.10.tar.gz
|
||||
b9ed12ca957c2f204ec37ea2836b924c36fab379 SOURCES/pcs-web-ui-0.1.7.tar.gz
|
||||
8824285e6f1c2807d9222d573c6e6df1e50d8410 SOURCES/pcs-web-ui-node-modules-0.1.7.tar.xz
|
||||
1937b826a36bb8396da227361d13f4c25830929c SOURCES/pcs-0.10.12.tar.gz
|
||||
a29bfd22130ac978c5d4a6a82108ce37ad2a5db9 SOURCES/pcs-web-ui-0.1.12.tar.gz
|
||||
c9723466d7bfb353899307a5700177f47e7e6cff SOURCES/pcs-web-ui-node-modules-0.1.12.tar.xz
|
||||
3176b2f2b332c2b6bf79fe882e83feecf3d3f011 SOURCES/pyagentx-0.4.pcs.2.tar.gz
|
||||
bd26127e57f83a10f656b62c46524c15aeb844dd SOURCES/python-dateutil-2.8.1.tar.gz
|
||||
345b7169d4d2d62176a225510399963bad62b68f SOURCES/rack-2.2.3.gem
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,73 @@
|
||||
From e5fc48f45a60228a82980dcd6d68ca01cf447eac Mon Sep 17 00:00:00 2001
|
||||
From: Ondrej Mular <omular@redhat.com>
|
||||
Date: Tue, 7 Dec 2021 11:58:09 +0100
|
||||
Subject: [PATCH 2/3] fix rsc update cmd when unable to get agent metadata
|
||||
|
||||
`resource update` command failed with a traceback when updating a
|
||||
resource with a non-existing resource agent
|
||||
---
|
||||
pcs/resource.py | 14 ++++++++------
|
||||
pcs_test/tier1/legacy/test_resource.py | 21 +++++++++++++++++++++
|
||||
2 files changed, 29 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/pcs/resource.py b/pcs/resource.py
|
||||
index c0e8b0d9..4514338d 100644
|
||||
--- a/pcs/resource.py
|
||||
+++ b/pcs/resource.py
|
||||
@@ -1049,13 +1049,15 @@ def resource_update(lib, args, modifiers, deal_with_guest_change=True):
|
||||
if report_list:
|
||||
process_library_reports(report_list)
|
||||
except lib_ra.ResourceAgentError as e:
|
||||
- severity = (
|
||||
- reports.ReportItemSeverity.WARNING
|
||||
- if modifiers.get("--force")
|
||||
- else reports.ReportItemSeverity.ERROR
|
||||
- )
|
||||
process_library_reports(
|
||||
- [lib_ra.resource_agent_error_to_report_item(e, severity)]
|
||||
+ [
|
||||
+ lib_ra.resource_agent_error_to_report_item(
|
||||
+ e,
|
||||
+ reports.get_severity(
|
||||
+ reports.codes.FORCE, modifiers.get("--force")
|
||||
+ ),
|
||||
+ )
|
||||
+ ]
|
||||
)
|
||||
except LibraryError as e:
|
||||
process_library_reports(e.args)
|
||||
diff --git a/pcs_test/tier1/legacy/test_resource.py b/pcs_test/tier1/legacy/test_resource.py
|
||||
index 3f0e08b9..bae0587a 100644
|
||||
--- a/pcs_test/tier1/legacy/test_resource.py
|
||||
+++ b/pcs_test/tier1/legacy/test_resource.py
|
||||
@@ -4879,6 +4879,27 @@ class UpdateInstanceAttrs(
|
||||
),
|
||||
)
|
||||
|
||||
+ def test_nonexisting_agent(self):
|
||||
+ agent = "ocf:pacemaker:nonexistent"
|
||||
+ message = (
|
||||
+ f"Agent '{agent}' is not installed or does "
|
||||
+ "not provide valid metadata: Metadata query for "
|
||||
+ f"{agent} failed: Input/output error"
|
||||
+ )
|
||||
+ self.assert_pcs_success(
|
||||
+ f"resource create --force D0 {agent}".split(),
|
||||
+ f"Warning: {message}\n",
|
||||
+ )
|
||||
+
|
||||
+ self.assert_pcs_fail(
|
||||
+ "resource update D0 test=testA".split(),
|
||||
+ f"Error: {message}, use --force to override\n",
|
||||
+ )
|
||||
+ self.assert_pcs_success(
|
||||
+ "resource update --force D0 test=testA".split(),
|
||||
+ f"Warning: {message}\n",
|
||||
+ )
|
||||
+
|
||||
def test_update_existing(self):
|
||||
xml = """
|
||||
<resources>
|
||||
--
|
||||
2.31.1
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,787 +0,0 @@
|
||||
From cf68ded959ad03244c94de308b79fc1af806a474 Mon Sep 17 00:00:00 2001
|
||||
From: Ondrej Mular <omular@redhat.com>
|
||||
Date: Wed, 15 Sep 2021 07:55:50 +0200
|
||||
Subject: [PATCH 1/2] fix unfencing in `pcs stonith update-scsi-devices`
|
||||
|
||||
* do not unfence newly added devices on fenced cluster nodes
|
||||
---
|
||||
pcs/common/reports/codes.py | 6 ++
|
||||
pcs/common/reports/messages.py | 41 +++++++
|
||||
pcs/lib/commands/scsi.py | 55 +++++++++-
|
||||
pcs/lib/commands/stonith.py | 26 +++--
|
||||
pcs/lib/communication/scsi.py | 40 ++++---
|
||||
.../tier0/common/reports/test_messages.py | 24 +++++
|
||||
pcs_test/tier0/lib/commands/test_scsi.py | 101 ++++++++++++++++--
|
||||
.../test_stonith_update_scsi_devices.py | 87 ++++++++++++---
|
||||
.../tools/command_env/config_http_scsi.py | 16 ++-
|
||||
.../tools/command_env/config_runner_scsi.py | 36 ++++++-
|
||||
pcsd/api_v1.rb | 2 +-
|
||||
pcsd/capabilities.xml | 8 +-
|
||||
12 files changed, 387 insertions(+), 55 deletions(-)
|
||||
|
||||
diff --git a/pcs/common/reports/codes.py b/pcs/common/reports/codes.py
|
||||
index bbd61500..4bee0bac 100644
|
||||
--- a/pcs/common/reports/codes.py
|
||||
+++ b/pcs/common/reports/codes.py
|
||||
@@ -468,6 +468,12 @@ STONITH_RESTARTLESS_UPDATE_UNSUPPORTED_AGENT = M(
|
||||
"STONITH_RESTARTLESS_UPDATE_UNSUPPORTED_AGENT"
|
||||
)
|
||||
STONITH_UNFENCING_FAILED = M("STONITH_UNFENCING_FAILED")
|
||||
+STONITH_UNFENCING_DEVICE_STATUS_FAILED = M(
|
||||
+ "STONITH_UNFENCING_DEVICE_STATUS_FAILED"
|
||||
+)
|
||||
+STONITH_UNFENCING_SKIPPED_DEVICES_FENCED = M(
|
||||
+ "STONITH_UNFENCING_SKIPPED_DEVICES_FENCED"
|
||||
+)
|
||||
STONITH_RESTARTLESS_UPDATE_UNABLE_TO_PERFORM = M(
|
||||
"STONITH_RESTARTLESS_UPDATE_UNABLE_TO_PERFORM"
|
||||
)
|
||||
diff --git a/pcs/common/reports/messages.py b/pcs/common/reports/messages.py
|
||||
index f9688437..be8dd154 100644
|
||||
--- a/pcs/common/reports/messages.py
|
||||
+++ b/pcs/common/reports/messages.py
|
||||
@@ -2782,6 +2782,47 @@ class StonithUnfencingFailed(ReportItemMessage):
|
||||
return f"Unfencing failed:\n{self.reason}"
|
||||
|
||||
|
||||
+@dataclass(frozen=True)
|
||||
+class StonithUnfencingDeviceStatusFailed(ReportItemMessage):
|
||||
+ """
|
||||
+ Unfencing failed on a cluster node.
|
||||
+ """
|
||||
+
|
||||
+ device: str
|
||||
+ reason: str
|
||||
+
|
||||
+ _code = codes.STONITH_UNFENCING_DEVICE_STATUS_FAILED
|
||||
+
|
||||
+ @property
|
||||
+ def message(self) -> str:
|
||||
+ return (
|
||||
+ "Unfencing failed, unable to check status of device "
|
||||
+ f"'{self.device}': {self.reason}"
|
||||
+ )
|
||||
+
|
||||
+
|
||||
+@dataclass(frozen=True)
|
||||
+class StonithUnfencingSkippedDevicesFenced(ReportItemMessage):
|
||||
+ """
|
||||
+ Unfencing skipped on a cluster node, because fenced devices were found on
|
||||
+ the node.
|
||||
+ """
|
||||
+
|
||||
+ devices: List[str]
|
||||
+
|
||||
+ _code = codes.STONITH_UNFENCING_SKIPPED_DEVICES_FENCED
|
||||
+
|
||||
+ @property
|
||||
+ def message(self) -> str:
|
||||
+ return (
|
||||
+ "Unfencing skipped, {device_pl} {devices} {is_pl} fenced"
|
||||
+ ).format(
|
||||
+ device_pl=format_plural(self.devices, "device"),
|
||||
+ devices=format_list(self.devices),
|
||||
+ is_pl=format_plural(self.devices, "is", "are"),
|
||||
+ )
|
||||
+
|
||||
+
|
||||
@dataclass(frozen=True)
|
||||
class StonithRestartlessUpdateUnableToPerform(ReportItemMessage):
|
||||
"""
|
||||
diff --git a/pcs/lib/commands/scsi.py b/pcs/lib/commands/scsi.py
|
||||
index 31a3ef2d..ff20a563 100644
|
||||
--- a/pcs/lib/commands/scsi.py
|
||||
+++ b/pcs/lib/commands/scsi.py
|
||||
@@ -8,20 +8,65 @@ from pcs.lib.env import LibraryEnvironment
|
||||
from pcs.lib.errors import LibraryError
|
||||
|
||||
|
||||
-def unfence_node(env: LibraryEnvironment, node: str, devices: Iterable[str]):
|
||||
+def unfence_node(
|
||||
+ env: LibraryEnvironment,
|
||||
+ node: str,
|
||||
+ original_devices: Iterable[str],
|
||||
+ updated_devices: Iterable[str],
|
||||
+) -> None:
|
||||
"""
|
||||
- Unfence scsi devices on a node by calling fence_scsi agent script.
|
||||
+ Unfence scsi devices on a node by calling fence_scsi agent script. Only
|
||||
+ newly added devices will be unfenced (set(updated_devices) -
|
||||
+ set(original_devices)). Before unfencing, original devices are be checked
|
||||
+ if any of them are not fenced. If there is a fenced device, unfencing will
|
||||
+ be skipped.
|
||||
|
||||
env -- provides communication with externals
|
||||
node -- node name on wich is unfencing performed
|
||||
- devices -- scsi devices to be unfenced
|
||||
+ original_devices -- list of devices defined before update
|
||||
+ updated_devices -- list of devices defined after update
|
||||
"""
|
||||
+ devices_to_unfence = set(updated_devices) - set(original_devices)
|
||||
+ if not devices_to_unfence:
|
||||
+ return
|
||||
+ fence_scsi_bin = os.path.join(settings.fence_agent_binaries, "fence_scsi")
|
||||
+ fenced_devices = []
|
||||
+ for device in original_devices:
|
||||
+ stdout, stderr, return_code = env.cmd_runner().run(
|
||||
+ [
|
||||
+ fence_scsi_bin,
|
||||
+ "--action=status",
|
||||
+ f"--devices={device}",
|
||||
+ f"--plug={node}",
|
||||
+ ]
|
||||
+ )
|
||||
+ if return_code == 2:
|
||||
+ fenced_devices.append(device)
|
||||
+ elif return_code != 0:
|
||||
+ raise LibraryError(
|
||||
+ reports.ReportItem.error(
|
||||
+ reports.messages.StonithUnfencingDeviceStatusFailed(
|
||||
+ device, join_multilines([stderr, stdout])
|
||||
+ )
|
||||
+ )
|
||||
+ )
|
||||
+ if fenced_devices:
|
||||
+ # At least one of existing devices is off, which means the node has
|
||||
+ # been fenced and new devices should not be unfenced.
|
||||
+ env.report_processor.report(
|
||||
+ reports.ReportItem.info(
|
||||
+ reports.messages.StonithUnfencingSkippedDevicesFenced(
|
||||
+ fenced_devices
|
||||
+ )
|
||||
+ )
|
||||
+ )
|
||||
+ return
|
||||
stdout, stderr, return_code = env.cmd_runner().run(
|
||||
[
|
||||
- os.path.join(settings.fence_agent_binaries, "fence_scsi"),
|
||||
+ fence_scsi_bin,
|
||||
"--action=on",
|
||||
"--devices",
|
||||
- ",".join(sorted(devices)),
|
||||
+ ",".join(sorted(devices_to_unfence)),
|
||||
f"--plug={node}",
|
||||
],
|
||||
)
|
||||
diff --git a/pcs/lib/commands/stonith.py b/pcs/lib/commands/stonith.py
|
||||
index 6f26e7d3..0dcf44f2 100644
|
||||
--- a/pcs/lib/commands/stonith.py
|
||||
+++ b/pcs/lib/commands/stonith.py
|
||||
@@ -453,7 +453,8 @@ def _update_scsi_devices_get_element_and_devices(
|
||||
|
||||
def _unfencing_scsi_devices(
|
||||
env: LibraryEnvironment,
|
||||
- device_list: Iterable[str],
|
||||
+ original_devices: Iterable[str],
|
||||
+ updated_devices: Iterable[str],
|
||||
force_flags: Container[reports.types.ForceCode] = (),
|
||||
) -> None:
|
||||
"""
|
||||
@@ -461,9 +462,13 @@ def _unfencing_scsi_devices(
|
||||
to pcsd and corosync is running.
|
||||
|
||||
env -- provides all for communication with externals
|
||||
- device_list -- devices to be unfenced
|
||||
+ original_devices -- devices before update
|
||||
+ updated_devices -- devices after update
|
||||
force_flags -- list of flags codes
|
||||
"""
|
||||
+ devices_to_unfence = set(updated_devices) - set(original_devices)
|
||||
+ if not devices_to_unfence:
|
||||
+ return
|
||||
cluster_nodes_names, nodes_report_list = get_existing_nodes_names(
|
||||
env.get_corosync_conf(),
|
||||
error_on_missing_name=True,
|
||||
@@ -487,7 +492,11 @@ def _unfencing_scsi_devices(
|
||||
online_corosync_target_list = run_and_raise(
|
||||
env.get_node_communicator(), com_cmd
|
||||
)
|
||||
- com_cmd = Unfence(env.report_processor, sorted(device_list))
|
||||
+ com_cmd = Unfence(
|
||||
+ env.report_processor,
|
||||
+ original_devices=sorted(original_devices),
|
||||
+ updated_devices=sorted(updated_devices),
|
||||
+ )
|
||||
com_cmd.set_targets(online_corosync_target_list)
|
||||
run_and_raise(env.get_node_communicator(), com_cmd)
|
||||
|
||||
@@ -531,9 +540,9 @@ def update_scsi_devices(
|
||||
IdProvider(stonith_el),
|
||||
set_device_list,
|
||||
)
|
||||
- devices_for_unfencing = set(set_device_list).difference(current_device_list)
|
||||
- if devices_for_unfencing:
|
||||
- _unfencing_scsi_devices(env, devices_for_unfencing, force_flags)
|
||||
+ _unfencing_scsi_devices(
|
||||
+ env, current_device_list, set_device_list, force_flags
|
||||
+ )
|
||||
env.push_cib()
|
||||
|
||||
|
||||
@@ -585,6 +594,7 @@ def update_scsi_devices_add_remove(
|
||||
IdProvider(stonith_el),
|
||||
updated_device_set,
|
||||
)
|
||||
- if add_device_list:
|
||||
- _unfencing_scsi_devices(env, add_device_list, force_flags)
|
||||
+ _unfencing_scsi_devices(
|
||||
+ env, current_device_list, updated_device_set, force_flags
|
||||
+ )
|
||||
env.push_cib()
|
||||
diff --git a/pcs/lib/communication/scsi.py b/pcs/lib/communication/scsi.py
|
||||
index 7b272017..250d67aa 100644
|
||||
--- a/pcs/lib/communication/scsi.py
|
||||
+++ b/pcs/lib/communication/scsi.py
|
||||
@@ -1,4 +1,5 @@
|
||||
import json
|
||||
+from typing import Iterable
|
||||
|
||||
from dacite import DaciteError
|
||||
|
||||
@@ -26,9 +27,15 @@ class Unfence(
|
||||
MarkSuccessfulMixin,
|
||||
RunRemotelyBase,
|
||||
):
|
||||
- def __init__(self, report_processor, devices):
|
||||
+ def __init__(
|
||||
+ self,
|
||||
+ report_processor: reports.ReportProcessor,
|
||||
+ original_devices: Iterable[str],
|
||||
+ updated_devices: Iterable[str],
|
||||
+ ) -> None:
|
||||
super().__init__(report_processor)
|
||||
- self._devices = devices
|
||||
+ self._original_devices = original_devices
|
||||
+ self._updated_devices = updated_devices
|
||||
|
||||
def _get_request_data(self):
|
||||
return None
|
||||
@@ -38,9 +45,13 @@ class Unfence(
|
||||
Request(
|
||||
target,
|
||||
RequestData(
|
||||
- "api/v1/scsi-unfence-node/v1",
|
||||
+ "api/v1/scsi-unfence-node/v2",
|
||||
data=json.dumps(
|
||||
- {"devices": self._devices, "node": target.label}
|
||||
+ dict(
|
||||
+ node=target.label,
|
||||
+ original_devices=self._original_devices,
|
||||
+ updated_devices=self._updated_devices,
|
||||
+ )
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -48,7 +59,9 @@ class Unfence(
|
||||
]
|
||||
|
||||
def _process_response(self, response):
|
||||
- report_item = response_to_report_item(response)
|
||||
+ report_item = response_to_report_item(
|
||||
+ response, report_pcsd_too_old_on_404=True
|
||||
+ )
|
||||
if report_item:
|
||||
self._report(report_item)
|
||||
return
|
||||
@@ -57,15 +70,14 @@ class Unfence(
|
||||
result = from_dict(
|
||||
InternalCommunicationResultDto, json.loads(response.data)
|
||||
)
|
||||
- if result.status != const.COM_STATUS_SUCCESS:
|
||||
- context = reports.ReportItemContext(node_label)
|
||||
- self._report_list(
|
||||
- [
|
||||
- reports.report_dto_to_item(report, context)
|
||||
- for report in result.report_list
|
||||
- ]
|
||||
- )
|
||||
- else:
|
||||
+ context = reports.ReportItemContext(node_label)
|
||||
+ self._report_list(
|
||||
+ [
|
||||
+ reports.report_dto_to_item(report, context)
|
||||
+ for report in result.report_list
|
||||
+ ]
|
||||
+ )
|
||||
+ if result.status == const.COM_STATUS_SUCCESS:
|
||||
self._on_success()
|
||||
|
||||
except (json.JSONDecodeError, DaciteError):
|
||||
diff --git a/pcs_test/tier0/common/reports/test_messages.py b/pcs_test/tier0/common/reports/test_messages.py
|
||||
index b0826cfd..05c3f619 100644
|
||||
--- a/pcs_test/tier0/common/reports/test_messages.py
|
||||
+++ b/pcs_test/tier0/common/reports/test_messages.py
|
||||
@@ -1904,6 +1904,30 @@ class StonithUnfencingFailed(NameBuildTest):
|
||||
)
|
||||
|
||||
|
||||
+class StonithUnfencingDeviceStatusFailed(NameBuildTest):
|
||||
+ def test_build_message(self):
|
||||
+ self.assert_message_from_report(
|
||||
+ "Unfencing failed, unable to check status of device 'dev1': reason",
|
||||
+ reports.StonithUnfencingDeviceStatusFailed("dev1", "reason"),
|
||||
+ )
|
||||
+
|
||||
+
|
||||
+class StonithUnfencingSkippedDevicesFenced(NameBuildTest):
|
||||
+ def test_one_device(self):
|
||||
+ self.assert_message_from_report(
|
||||
+ "Unfencing skipped, device 'dev1' is fenced",
|
||||
+ reports.StonithUnfencingSkippedDevicesFenced(["dev1"]),
|
||||
+ )
|
||||
+
|
||||
+ def test_multiple_devices(self):
|
||||
+ self.assert_message_from_report(
|
||||
+ "Unfencing skipped, devices 'dev1', 'dev2', 'dev3' are fenced",
|
||||
+ reports.StonithUnfencingSkippedDevicesFenced(
|
||||
+ ["dev2", "dev1", "dev3"]
|
||||
+ ),
|
||||
+ )
|
||||
+
|
||||
+
|
||||
class StonithRestartlessUpdateUnableToPerform(NameBuildTest):
|
||||
def test_build_message(self):
|
||||
self.assert_message_from_report(
|
||||
diff --git a/pcs_test/tier0/lib/commands/test_scsi.py b/pcs_test/tier0/lib/commands/test_scsi.py
|
||||
index de75743f..8ef9836a 100644
|
||||
--- a/pcs_test/tier0/lib/commands/test_scsi.py
|
||||
+++ b/pcs_test/tier0/lib/commands/test_scsi.py
|
||||
@@ -10,26 +10,113 @@ from pcs.lib.commands import scsi
|
||||
class TestUnfenceNode(TestCase):
|
||||
def setUp(self):
|
||||
self.env_assist, self.config = get_env_tools(self)
|
||||
+ self.old_devices = ["device1", "device3"]
|
||||
+ self.new_devices = ["device3", "device0", "device2"]
|
||||
+ self.added_devices = set(self.new_devices) - set(self.old_devices)
|
||||
+ self.node = "node1"
|
||||
|
||||
- def test_success(self):
|
||||
- self.config.runner.scsi.unfence_node("node1", ["/dev/sda", "/dev/sdb"])
|
||||
+ def test_success_devices_to_unfence(self):
|
||||
+ for old_dev in self.old_devices:
|
||||
+ self.config.runner.scsi.get_status(
|
||||
+ self.node, old_dev, name=f"runner.scsi.is_fenced.{old_dev}"
|
||||
+ )
|
||||
+ self.config.runner.scsi.unfence_node(self.node, self.added_devices)
|
||||
scsi.unfence_node(
|
||||
- self.env_assist.get_env(), "node1", ["/dev/sdb", "/dev/sda"]
|
||||
+ self.env_assist.get_env(),
|
||||
+ self.node,
|
||||
+ self.old_devices,
|
||||
+ self.new_devices,
|
||||
)
|
||||
self.env_assist.assert_reports([])
|
||||
|
||||
- def test_failure(self):
|
||||
+ def test_success_no_devices_to_unfence(self):
|
||||
+ scsi.unfence_node(
|
||||
+ self.env_assist.get_env(),
|
||||
+ self.node,
|
||||
+ {"device1", "device2", "device3"},
|
||||
+ {"device3"},
|
||||
+ )
|
||||
+ self.env_assist.assert_reports([])
|
||||
+
|
||||
+ def test_unfencing_failure(self):
|
||||
+ err_msg = "stderr"
|
||||
+ for old_dev in self.old_devices:
|
||||
+ self.config.runner.scsi.get_status(
|
||||
+ self.node, old_dev, name=f"runner.scsi.is_fenced.{old_dev}"
|
||||
+ )
|
||||
self.config.runner.scsi.unfence_node(
|
||||
- "node1", ["/dev/sda", "/dev/sdb"], stderr="stderr", return_code=1
|
||||
+ self.node, self.added_devices, stderr=err_msg, return_code=1
|
||||
)
|
||||
self.env_assist.assert_raise_library_error(
|
||||
lambda: scsi.unfence_node(
|
||||
- self.env_assist.get_env(), "node1", ["/dev/sdb", "/dev/sda"]
|
||||
+ self.env_assist.get_env(),
|
||||
+ self.node,
|
||||
+ self.old_devices,
|
||||
+ self.new_devices,
|
||||
),
|
||||
[
|
||||
fixture.error(
|
||||
- report_codes.STONITH_UNFENCING_FAILED, reason="stderr"
|
||||
+ report_codes.STONITH_UNFENCING_FAILED, reason=err_msg
|
||||
)
|
||||
],
|
||||
expected_in_processor=False,
|
||||
)
|
||||
+
|
||||
+ def test_device_status_failed(self):
|
||||
+ err_msg = "stderr"
|
||||
+ new_devices = ["device1", "device2", "device3", "device4"]
|
||||
+ old_devices = new_devices[:-1]
|
||||
+ ok_devices = new_devices[0:2]
|
||||
+ err_device = new_devices[2]
|
||||
+ for dev in ok_devices:
|
||||
+ self.config.runner.scsi.get_status(
|
||||
+ self.node, dev, name=f"runner.scsi.is_fenced.{dev}"
|
||||
+ )
|
||||
+ self.config.runner.scsi.get_status(
|
||||
+ self.node,
|
||||
+ err_device,
|
||||
+ name=f"runner.scsi.is_fenced.{err_device}",
|
||||
+ stderr=err_msg,
|
||||
+ return_code=1,
|
||||
+ )
|
||||
+ self.env_assist.assert_raise_library_error(
|
||||
+ lambda: scsi.unfence_node(
|
||||
+ self.env_assist.get_env(),
|
||||
+ self.node,
|
||||
+ old_devices,
|
||||
+ new_devices,
|
||||
+ ),
|
||||
+ [
|
||||
+ fixture.error(
|
||||
+ report_codes.STONITH_UNFENCING_DEVICE_STATUS_FAILED,
|
||||
+ device=err_device,
|
||||
+ reason=err_msg,
|
||||
+ )
|
||||
+ ],
|
||||
+ expected_in_processor=False,
|
||||
+ )
|
||||
+
|
||||
+ def test_unfencing_skipped_devices_are_fenced(self):
|
||||
+ stdout_off = "Status: OFF"
|
||||
+ for old_dev in self.old_devices:
|
||||
+ self.config.runner.scsi.get_status(
|
||||
+ self.node,
|
||||
+ old_dev,
|
||||
+ name=f"runner.scsi.is_fenced.{old_dev}",
|
||||
+ stdout=stdout_off,
|
||||
+ return_code=2,
|
||||
+ )
|
||||
+ scsi.unfence_node(
|
||||
+ self.env_assist.get_env(),
|
||||
+ self.node,
|
||||
+ self.old_devices,
|
||||
+ self.new_devices,
|
||||
+ )
|
||||
+ self.env_assist.assert_reports(
|
||||
+ [
|
||||
+ fixture.info(
|
||||
+ report_codes.STONITH_UNFENCING_SKIPPED_DEVICES_FENCED,
|
||||
+ devices=sorted(self.old_devices),
|
||||
+ )
|
||||
+ ]
|
||||
+ )
|
||||
diff --git a/pcs_test/tier0/lib/commands/test_stonith_update_scsi_devices.py b/pcs_test/tier0/lib/commands/test_stonith_update_scsi_devices.py
|
||||
index 6ff6b99a..ed8f5d4f 100644
|
||||
--- a/pcs_test/tier0/lib/commands/test_stonith_update_scsi_devices.py
|
||||
+++ b/pcs_test/tier0/lib/commands/test_stonith_update_scsi_devices.py
|
||||
@@ -1,3 +1,4 @@
|
||||
+# pylint: disable=too-many-lines
|
||||
import json
|
||||
from unittest import mock, TestCase
|
||||
|
||||
@@ -297,7 +298,9 @@ class UpdateScsiDevicesMixin:
|
||||
node_labels=self.existing_nodes
|
||||
)
|
||||
self.config.http.scsi.unfence_node(
|
||||
- unfence, node_labels=self.existing_nodes
|
||||
+ original_devices=devices_before,
|
||||
+ updated_devices=devices_updated,
|
||||
+ node_labels=self.existing_nodes,
|
||||
)
|
||||
self.config.env.push_cib(
|
||||
resources=fixture_scsi(
|
||||
@@ -449,14 +452,14 @@ class UpdateScsiDevicesFailuresMixin:
|
||||
node_labels=self.existing_nodes
|
||||
)
|
||||
self.config.http.scsi.unfence_node(
|
||||
- DEVICES_2,
|
||||
communication_list=[
|
||||
dict(
|
||||
label=self.existing_nodes[0],
|
||||
raw_data=json.dumps(
|
||||
dict(
|
||||
- devices=[DEV_2],
|
||||
node=self.existing_nodes[0],
|
||||
+ original_devices=DEVICES_1,
|
||||
+ updated_devices=DEVICES_2,
|
||||
)
|
||||
),
|
||||
was_connected=False,
|
||||
@@ -466,8 +469,9 @@ class UpdateScsiDevicesFailuresMixin:
|
||||
label=self.existing_nodes[1],
|
||||
raw_data=json.dumps(
|
||||
dict(
|
||||
- devices=[DEV_2],
|
||||
node=self.existing_nodes[1],
|
||||
+ original_devices=DEVICES_1,
|
||||
+ updated_devices=DEVICES_2,
|
||||
)
|
||||
),
|
||||
output=json.dumps(
|
||||
@@ -491,8 +495,9 @@ class UpdateScsiDevicesFailuresMixin:
|
||||
label=self.existing_nodes[2],
|
||||
raw_data=json.dumps(
|
||||
dict(
|
||||
- devices=[DEV_2],
|
||||
node=self.existing_nodes[2],
|
||||
+ original_devices=DEVICES_1,
|
||||
+ updated_devices=DEVICES_2,
|
||||
)
|
||||
),
|
||||
),
|
||||
@@ -504,7 +509,7 @@ class UpdateScsiDevicesFailuresMixin:
|
||||
fixture.error(
|
||||
reports.codes.NODE_COMMUNICATION_ERROR_UNABLE_TO_CONNECT,
|
||||
node=self.existing_nodes[0],
|
||||
- command="api/v1/scsi-unfence-node/v1",
|
||||
+ command="api/v1/scsi-unfence-node/v2",
|
||||
reason="errA",
|
||||
),
|
||||
fixture.error(
|
||||
@@ -517,20 +522,76 @@ class UpdateScsiDevicesFailuresMixin:
|
||||
]
|
||||
)
|
||||
|
||||
+ def test_unfence_failure_unknown_command(self):
|
||||
+ self._unfence_failure_common_calls()
|
||||
+ self.config.http.corosync.get_corosync_online_targets(
|
||||
+ node_labels=self.existing_nodes
|
||||
+ )
|
||||
+ communication_list = [
|
||||
+ dict(
|
||||
+ label=node,
|
||||
+ raw_data=json.dumps(
|
||||
+ dict(
|
||||
+ node=node,
|
||||
+ original_devices=DEVICES_1,
|
||||
+ updated_devices=DEVICES_2,
|
||||
+ )
|
||||
+ ),
|
||||
+ )
|
||||
+ for node in self.existing_nodes[0:2]
|
||||
+ ]
|
||||
+ communication_list.append(
|
||||
+ dict(
|
||||
+ label=self.existing_nodes[2],
|
||||
+ response_code=404,
|
||||
+ raw_data=json.dumps(
|
||||
+ dict(
|
||||
+ node=self.existing_nodes[2],
|
||||
+ original_devices=DEVICES_1,
|
||||
+ updated_devices=DEVICES_2,
|
||||
+ )
|
||||
+ ),
|
||||
+ output=json.dumps(
|
||||
+ dto.to_dict(
|
||||
+ communication.dto.InternalCommunicationResultDto(
|
||||
+ status=communication.const.COM_STATUS_UNKNOWN_CMD,
|
||||
+ status_msg=(
|
||||
+ "Unknown command '/api/v1/scsi-unfence-node/v2'"
|
||||
+ ),
|
||||
+ report_list=[],
|
||||
+ data=None,
|
||||
+ )
|
||||
+ )
|
||||
+ ),
|
||||
+ ),
|
||||
+ )
|
||||
+ self.config.http.scsi.unfence_node(
|
||||
+ communication_list=communication_list
|
||||
+ )
|
||||
+ self.env_assist.assert_raise_library_error(self.command())
|
||||
+ self.env_assist.assert_reports(
|
||||
+ [
|
||||
+ fixture.error(
|
||||
+ reports.codes.PCSD_VERSION_TOO_OLD,
|
||||
+ node=self.existing_nodes[2],
|
||||
+ ),
|
||||
+ ]
|
||||
+ )
|
||||
+
|
||||
def test_unfence_failure_agent_script_failed(self):
|
||||
self._unfence_failure_common_calls()
|
||||
self.config.http.corosync.get_corosync_online_targets(
|
||||
node_labels=self.existing_nodes
|
||||
)
|
||||
self.config.http.scsi.unfence_node(
|
||||
- DEVICES_2,
|
||||
communication_list=[
|
||||
dict(
|
||||
label=self.existing_nodes[0],
|
||||
raw_data=json.dumps(
|
||||
dict(
|
||||
- devices=[DEV_2],
|
||||
node=self.existing_nodes[0],
|
||||
+ original_devices=DEVICES_1,
|
||||
+ updated_devices=DEVICES_2,
|
||||
)
|
||||
),
|
||||
),
|
||||
@@ -538,8 +599,9 @@ class UpdateScsiDevicesFailuresMixin:
|
||||
label=self.existing_nodes[1],
|
||||
raw_data=json.dumps(
|
||||
dict(
|
||||
- devices=[DEV_2],
|
||||
node=self.existing_nodes[1],
|
||||
+ original_devices=DEVICES_1,
|
||||
+ updated_devices=DEVICES_2,
|
||||
)
|
||||
),
|
||||
output=json.dumps(
|
||||
@@ -563,8 +625,9 @@ class UpdateScsiDevicesFailuresMixin:
|
||||
label=self.existing_nodes[2],
|
||||
raw_data=json.dumps(
|
||||
dict(
|
||||
- devices=[DEV_2],
|
||||
node=self.existing_nodes[2],
|
||||
+ original_devices=DEVICES_1,
|
||||
+ updated_devices=DEVICES_2,
|
||||
)
|
||||
),
|
||||
),
|
||||
@@ -639,14 +702,14 @@ class UpdateScsiDevicesFailuresMixin:
|
||||
]
|
||||
)
|
||||
self.config.http.scsi.unfence_node(
|
||||
- DEVICES_2,
|
||||
communication_list=[
|
||||
dict(
|
||||
label=self.existing_nodes[0],
|
||||
raw_data=json.dumps(
|
||||
dict(
|
||||
- devices=[DEV_2],
|
||||
node=self.existing_nodes[0],
|
||||
+ original_devices=DEVICES_1,
|
||||
+ updated_devices=DEVICES_2,
|
||||
)
|
||||
),
|
||||
),
|
||||
diff --git a/pcs_test/tools/command_env/config_http_scsi.py b/pcs_test/tools/command_env/config_http_scsi.py
|
||||
index 0e9f63af..7150eef9 100644
|
||||
--- a/pcs_test/tools/command_env/config_http_scsi.py
|
||||
+++ b/pcs_test/tools/command_env/config_http_scsi.py
|
||||
@@ -14,7 +14,8 @@ class ScsiShortcuts:
|
||||
|
||||
def unfence_node(
|
||||
self,
|
||||
- devices,
|
||||
+ original_devices=(),
|
||||
+ updated_devices=(),
|
||||
node_labels=None,
|
||||
communication_list=None,
|
||||
name="http.scsi.unfence_node",
|
||||
@@ -22,7 +23,8 @@ class ScsiShortcuts:
|
||||
"""
|
||||
Create a calls for node unfencing
|
||||
|
||||
- list devices -- list of scsi devices
|
||||
+ list original_devices -- list of scsi devices before an update
|
||||
+ list updated_devices -- list of scsi devices after an update
|
||||
list node_labels -- create success responses from these nodes
|
||||
list communication_list -- use these custom responses
|
||||
string name -- the key of this call
|
||||
@@ -39,7 +41,13 @@ class ScsiShortcuts:
|
||||
communication_list = [
|
||||
dict(
|
||||
label=node,
|
||||
- raw_data=json.dumps(dict(devices=devices, node=node)),
|
||||
+ raw_data=json.dumps(
|
||||
+ dict(
|
||||
+ node=node,
|
||||
+ original_devices=original_devices,
|
||||
+ updated_devices=updated_devices,
|
||||
+ )
|
||||
+ ),
|
||||
)
|
||||
for node in node_labels
|
||||
]
|
||||
@@ -47,7 +55,7 @@ class ScsiShortcuts:
|
||||
self.__calls,
|
||||
name,
|
||||
communication_list,
|
||||
- action="api/v1/scsi-unfence-node/v1",
|
||||
+ action="api/v1/scsi-unfence-node/v2",
|
||||
output=json.dumps(
|
||||
to_dict(
|
||||
communication.dto.InternalCommunicationResultDto(
|
||||
diff --git a/pcs_test/tools/command_env/config_runner_scsi.py b/pcs_test/tools/command_env/config_runner_scsi.py
|
||||
index 4b671bb7..3cee13d6 100644
|
||||
--- a/pcs_test/tools/command_env/config_runner_scsi.py
|
||||
+++ b/pcs_test/tools/command_env/config_runner_scsi.py
|
||||
@@ -35,7 +35,41 @@ class ScsiShortcuts:
|
||||
os.path.join(settings.fence_agent_binaries, "fence_scsi"),
|
||||
"--action=on",
|
||||
"--devices",
|
||||
- ",".join(devices),
|
||||
+ ",".join(sorted(devices)),
|
||||
+ f"--plug={node}",
|
||||
+ ],
|
||||
+ stdout=stdout,
|
||||
+ stderr=stderr,
|
||||
+ returncode=return_code,
|
||||
+ ),
|
||||
+ )
|
||||
+
|
||||
+ def get_status(
|
||||
+ self,
|
||||
+ node,
|
||||
+ device,
|
||||
+ stdout="",
|
||||
+ stderr="",
|
||||
+ return_code=0,
|
||||
+ name="runner.scsi.is_fenced",
|
||||
+ ):
|
||||
+ """
|
||||
+ Create a call for getting scsi status
|
||||
+
|
||||
+ string node -- a node from which is unfencing performed
|
||||
+ str device -- a device to check
|
||||
+ string stdout -- stdout from fence_scsi agent script
|
||||
+ string stderr -- stderr from fence_scsi agent script
|
||||
+ int return_code -- return code of the fence_scsi agent script
|
||||
+ string name -- the key of this call
|
||||
+ """
|
||||
+ self.__calls.place(
|
||||
+ name,
|
||||
+ RunnerCall(
|
||||
+ [
|
||||
+ os.path.join(settings.fence_agent_binaries, "fence_scsi"),
|
||||
+ "--action=status",
|
||||
+ f"--devices={device}",
|
||||
f"--plug={node}",
|
||||
],
|
||||
stdout=stdout,
|
||||
diff --git a/pcsd/api_v1.rb b/pcsd/api_v1.rb
|
||||
index 7edeeabf..e55c2be7 100644
|
||||
--- a/pcsd/api_v1.rb
|
||||
+++ b/pcsd/api_v1.rb
|
||||
@@ -291,7 +291,7 @@ def route_api_v1(auth_user, params, request)
|
||||
:only_superuser => false,
|
||||
:permissions => Permissions::WRITE,
|
||||
},
|
||||
- 'scsi-unfence-node/v1' => {
|
||||
+ 'scsi-unfence-node/v2' => {
|
||||
:cmd => 'scsi.unfence_node',
|
||||
:only_superuser => false,
|
||||
:permissions => Permissions::WRITE,
|
||||
diff --git a/pcsd/capabilities.xml b/pcsd/capabilities.xml
|
||||
index 58ebcf0f..3954aa5d 100644
|
||||
--- a/pcsd/capabilities.xml
|
||||
+++ b/pcsd/capabilities.xml
|
||||
@@ -1892,11 +1892,13 @@
|
||||
pcs commands: stonith update-scsi-devices
|
||||
</description>
|
||||
</capability>
|
||||
- <capability id="pcmk.stonith.scsi-unfence-node" in-pcs="0" in-pcsd="1">
|
||||
+ <capability id="pcmk.stonith.scsi-unfence-node-v2" in-pcs="0" in-pcsd="1">
|
||||
<description>
|
||||
- Unfence scsi devices on a cluster node.
|
||||
+ Unfence scsi devices on a cluster node. In comparison with v1, only
|
||||
+ newly added devices are unfenced. In case any existing device is
|
||||
+ fenced, unfencing will be skipped.
|
||||
|
||||
- daemon urls: /api/v1/scsi-unfence-node/v1
|
||||
+ daemon urls: /api/v1/scsi-unfence-node/v2
|
||||
</description>
|
||||
</capability>
|
||||
<capability id="pcmk.stonith.enable-disable" in-pcs="1" in-pcsd="1">
|
||||
--
|
||||
2.31.1
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,45 +0,0 @@
|
||||
From 189c73e31f5033413fc4483e40d0bfc78d77f962 Mon Sep 17 00:00:00 2001
|
||||
From: Tomas Jelinek <tojeline@redhat.com>
|
||||
Date: Fri, 27 Aug 2021 12:05:18 +0200
|
||||
Subject: [PATCH 1/2] fix creating resources with depth operation attribute
|
||||
|
||||
---
|
||||
CHANGELOG.md | 9 +++++++++
|
||||
pcs/lib/cib/resource/operations.py | 2 +-
|
||||
2 files changed, 10 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/CHANGELOG.md b/CHANGELOG.md
|
||||
index f768cc36..c15546ba 100644
|
||||
--- a/CHANGELOG.md
|
||||
+++ b/CHANGELOG.md
|
||||
@@ -1,5 +1,14 @@
|
||||
# Change Log
|
||||
|
||||
+## [Unreleased]
|
||||
+
|
||||
+### Fixed
|
||||
+- Fixed an error when creating a resource which defines 'depth' attribute for
|
||||
+ its operations ([rhbz#1998454])
|
||||
+
|
||||
+[rhbz#1998454]: https://bugzilla.redhat.com/show_bug.cgi?id=1998454
|
||||
+
|
||||
+
|
||||
## [0.10.10] - 2021-08-19
|
||||
|
||||
### Added
|
||||
diff --git a/pcs/lib/cib/resource/operations.py b/pcs/lib/cib/resource/operations.py
|
||||
index 390db71a..44b2e7dd 100644
|
||||
--- a/pcs/lib/cib/resource/operations.py
|
||||
+++ b/pcs/lib/cib/resource/operations.py
|
||||
@@ -197,7 +197,7 @@ def _action_dto_to_dict(
|
||||
) -> Dict[str, str]:
|
||||
result = dict(
|
||||
filter(
|
||||
- lambda item: item[0] != "deph" and item[1] not in (None, ""),
|
||||
+ lambda item: item[0] != "depth" and item[1] not in (None, ""),
|
||||
to_dict(dto).items(),
|
||||
)
|
||||
)
|
||||
--
|
||||
2.31.1
|
||||
|
@ -1,7 +1,7 @@
|
||||
From eae00a30e6eb682e60ec1ace4ec6633591254e15 Mon Sep 17 00:00:00 2001
|
||||
From f0342f110bdb4a7421532b85ca0f49070c7e5c1e Mon Sep 17 00:00:00 2001
|
||||
From: Tomas Jelinek <tojeline@redhat.com>
|
||||
Date: Thu, 13 Jan 2022 17:32:38 +0100
|
||||
Subject: [PATCH] fix creating empty cib
|
||||
Subject: [PATCH 4/5] fix creating empty cib
|
||||
|
||||
---
|
||||
pcs/utils.py | 21 +++++++++++----------
|
25
SOURCES/bz2028902-01-fix-enabling-corosync-qdevice.patch
Normal file
25
SOURCES/bz2028902-01-fix-enabling-corosync-qdevice.patch
Normal file
@ -0,0 +1,25 @@
|
||||
From 6b4b0c0026e5077044e4e908d093cb613ae2e94e Mon Sep 17 00:00:00 2001
|
||||
From: Tomas Jelinek <tojeline@redhat.com>
|
||||
Date: Mon, 6 Dec 2021 16:06:31 +0100
|
||||
Subject: [PATCH 1/3] fix enabling corosync-qdevice
|
||||
|
||||
---
|
||||
pcsd/remote.rb | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/pcsd/remote.rb b/pcsd/remote.rb
|
||||
index c49db116..3574d665 100644
|
||||
--- a/pcsd/remote.rb
|
||||
+++ b/pcsd/remote.rb
|
||||
@@ -2515,7 +2515,7 @@ def qdevice_client_enable(param, request, auth_user)
|
||||
unless allowed_for_local_cluster(auth_user, Permissions::WRITE)
|
||||
return 403, 'Permission denied'
|
||||
end
|
||||
- if not ServiceChecker.new('corosync', enabled: true).is_enabled?('corosync')
|
||||
+ if not ServiceChecker.new(['corosync'], enabled: true).is_enabled?('corosync')
|
||||
return pcsd_success('corosync is not enabled, skipping')
|
||||
elsif enable_service('corosync-qdevice')
|
||||
return pcsd_success('corosync-qdevice enabled')
|
||||
--
|
||||
2.31.1
|
||||
|
@ -0,0 +1,86 @@
|
||||
From 082bded126151e4f4b4667a1d8337db741828da6 Mon Sep 17 00:00:00 2001
|
||||
From: Miroslav Lisik <mlisik@redhat.com>
|
||||
Date: Thu, 16 Dec 2021 14:12:58 +0100
|
||||
Subject: [PATCH 1/5] skip checking of scsi devices to be removed before
|
||||
unfencing to be added devices
|
||||
|
||||
---
|
||||
pcs/lib/commands/scsi.py | 3 ++-
|
||||
pcs_test/tier0/lib/commands/test_scsi.py | 21 +++++++++++++++++----
|
||||
2 files changed, 19 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/pcs/lib/commands/scsi.py b/pcs/lib/commands/scsi.py
|
||||
index ff20a563..ab732805 100644
|
||||
--- a/pcs/lib/commands/scsi.py
|
||||
+++ b/pcs/lib/commands/scsi.py
|
||||
@@ -31,7 +31,8 @@ def unfence_node(
|
||||
return
|
||||
fence_scsi_bin = os.path.join(settings.fence_agent_binaries, "fence_scsi")
|
||||
fenced_devices = []
|
||||
- for device in original_devices:
|
||||
+ # do not check devices being removed
|
||||
+ for device in sorted(set(original_devices) & set(updated_devices)):
|
||||
stdout, stderr, return_code = env.cmd_runner().run(
|
||||
[
|
||||
fence_scsi_bin,
|
||||
diff --git a/pcs_test/tier0/lib/commands/test_scsi.py b/pcs_test/tier0/lib/commands/test_scsi.py
|
||||
index 8ef9836a..bc2357a9 100644
|
||||
--- a/pcs_test/tier0/lib/commands/test_scsi.py
|
||||
+++ b/pcs_test/tier0/lib/commands/test_scsi.py
|
||||
@@ -13,10 +13,13 @@ class TestUnfenceNode(TestCase):
|
||||
self.old_devices = ["device1", "device3"]
|
||||
self.new_devices = ["device3", "device0", "device2"]
|
||||
self.added_devices = set(self.new_devices) - set(self.old_devices)
|
||||
+ self.check_devices = sorted(
|
||||
+ set(self.old_devices) & set(self.new_devices)
|
||||
+ )
|
||||
self.node = "node1"
|
||||
|
||||
def test_success_devices_to_unfence(self):
|
||||
- for old_dev in self.old_devices:
|
||||
+ for old_dev in self.check_devices:
|
||||
self.config.runner.scsi.get_status(
|
||||
self.node, old_dev, name=f"runner.scsi.is_fenced.{old_dev}"
|
||||
)
|
||||
@@ -38,9 +41,19 @@ class TestUnfenceNode(TestCase):
|
||||
)
|
||||
self.env_assist.assert_reports([])
|
||||
|
||||
+ def test_success_replace_unavailable_device(self):
|
||||
+ self.config.runner.scsi.unfence_node(self.node, {"device2"})
|
||||
+ scsi.unfence_node(
|
||||
+ self.env_assist.get_env(),
|
||||
+ self.node,
|
||||
+ {"device1"},
|
||||
+ {"device2"},
|
||||
+ )
|
||||
+ self.env_assist.assert_reports([])
|
||||
+
|
||||
def test_unfencing_failure(self):
|
||||
err_msg = "stderr"
|
||||
- for old_dev in self.old_devices:
|
||||
+ for old_dev in self.check_devices:
|
||||
self.config.runner.scsi.get_status(
|
||||
self.node, old_dev, name=f"runner.scsi.is_fenced.{old_dev}"
|
||||
)
|
||||
@@ -98,7 +111,7 @@ class TestUnfenceNode(TestCase):
|
||||
|
||||
def test_unfencing_skipped_devices_are_fenced(self):
|
||||
stdout_off = "Status: OFF"
|
||||
- for old_dev in self.old_devices:
|
||||
+ for old_dev in self.check_devices:
|
||||
self.config.runner.scsi.get_status(
|
||||
self.node,
|
||||
old_dev,
|
||||
@@ -116,7 +129,7 @@ class TestUnfenceNode(TestCase):
|
||||
[
|
||||
fixture.info(
|
||||
report_codes.STONITH_UNFENCING_SKIPPED_DEVICES_FENCED,
|
||||
- devices=sorted(self.old_devices),
|
||||
+ devices=sorted(self.check_devices),
|
||||
)
|
||||
]
|
||||
)
|
||||
--
|
||||
2.31.1
|
||||
|
@ -0,0 +1,41 @@
|
||||
From 46b079a93d1817f9c1d6a7403c70b30f59d19c20 Mon Sep 17 00:00:00 2001
|
||||
From: Tomas Jelinek <tojeline@redhat.com>
|
||||
Date: Tue, 4 Jan 2022 12:56:56 +0100
|
||||
Subject: [PATCH 2/5] Make ocf:linbit:drbd agent pass OCF validation
|
||||
|
||||
---
|
||||
data/ocf-1.0.rng | 18 ++++++++----------
|
||||
1 file changed, 8 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/data/ocf-1.0.rng b/data/ocf-1.0.rng
|
||||
index 36ba4611..1e14a83b 100644
|
||||
--- a/data/ocf-1.0.rng
|
||||
+++ b/data/ocf-1.0.rng
|
||||
@@ -169,16 +169,14 @@ RNGs. Thank you.
|
||||
<optional>
|
||||
<element name="content">
|
||||
<choice>
|
||||
- <attribute name="type">
|
||||
- <choice>
|
||||
- <value>boolean</value>
|
||||
- <value>string</value>
|
||||
- <value>integer</value>
|
||||
- <value>second</value><!-- used by fence agents -->
|
||||
- <value>int</value><!-- used by fence agents intead of integer -->
|
||||
- <value>time</value><!-- used by pacemaker metadata -->
|
||||
- </choice>
|
||||
- </attribute>
|
||||
+ <!--
|
||||
+ OCF 1.0 allows values: boolean, integer, string. Agents, however,
|
||||
+ quite often use other values: int (fence agents), numeric
|
||||
+ (ocf:linbit:drbd), second (fence agents), time (pacemaker
|
||||
+ metadata). Since pcs doesn't actually care about the type, we
|
||||
+ allow any type to keep compatibility with existing agents.
|
||||
+ -->
|
||||
+ <attribute name="type" />
|
||||
<group>
|
||||
<!--
|
||||
used by fence agents and processed by pcs even though it is not
|
||||
--
|
||||
2.31.1
|
||||
|
@ -0,0 +1,23 @@
|
||||
From fa75f40361bc39cbd645b8014713e4c0ad0cda18 Mon Sep 17 00:00:00 2001
|
||||
From: Ivan Devat <idevat@redhat.com>
|
||||
Date: Mon, 24 Jan 2022 14:08:54 +0100
|
||||
Subject: [PATCH 2/2] fix backend parameter "all" in cluster destroy
|
||||
|
||||
---
|
||||
src/app/backend/calls/destroyCluster.ts | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/app/backend/calls/destroyCluster.ts b/src/app/backend/calls/destroyCluster.ts
|
||||
index b6e83a41..cf41ea42 100644
|
||||
--- a/src/app/backend/calls/destroyCluster.ts
|
||||
+++ b/src/app/backend/calls/destroyCluster.ts
|
||||
@@ -4,5 +4,5 @@ const { url } = endpoints.destroyCluster;
|
||||
|
||||
export const destroyCluster = (clusterName: string): CallResult =>
|
||||
http.post(url({ clusterName }), {
|
||||
- params: [["--all", "1"]],
|
||||
+ params: [["all", "1"]],
|
||||
});
|
||||
--
|
||||
2.31.1
|
||||
|
25
SOURCES/bz2047983-01-Fix-snmp-client.patch
Normal file
25
SOURCES/bz2047983-01-Fix-snmp-client.patch
Normal file
@ -0,0 +1,25 @@
|
||||
From 68aa09a89804084e2764b06f0ae37f56cc609bda Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Valentin=20Vidi=C4=87?= <vvidic@valentin-vidic.from.hr>
|
||||
Date: Wed, 15 Dec 2021 20:32:26 +0100
|
||||
Subject: [PATCH 1/2] Fix snmp client
|
||||
|
||||
Required constant is missing causing the command to fail on startup and breaking the pcs_snmp_agent service.
|
||||
---
|
||||
pcsd/pcsd-cli-main.rb | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
diff --git a/pcsd/pcsd-cli-main.rb b/pcsd/pcsd-cli-main.rb
|
||||
index 29b9006d..be72d543 100644
|
||||
--- a/pcsd/pcsd-cli-main.rb
|
||||
+++ b/pcsd/pcsd-cli-main.rb
|
||||
@@ -10,6 +10,7 @@ require 'remote.rb'
|
||||
|
||||
|
||||
PCS = get_pcs_path()
|
||||
+PCS_INTERNAL = get_pcs_internal_path()
|
||||
$logger_device = StringIO.new
|
||||
$logger = Logger.new($logger_device)
|
||||
early_log($logger)
|
||||
--
|
||||
2.34.1
|
||||
|
@ -0,0 +1,934 @@
|
||||
From ae3435418f0af6e5f22f463871aa90a5c5b2d15f Mon Sep 17 00:00:00 2001
|
||||
From: Tomas Jelinek <tojeline@redhat.com>
|
||||
Date: Fri, 4 Feb 2022 16:23:18 +0100
|
||||
Subject: [PATCH 1/3] process invalid OCF agents as if they complied with OCF
|
||||
1.0
|
||||
|
||||
---
|
||||
pcs/common/reports/codes.py | 4 +-
|
||||
pcs/common/reports/messages.py | 13 +-
|
||||
pcs/lib/commands/resource.py | 3 +-
|
||||
pcs/lib/commands/resource_agent.py | 4 +-
|
||||
pcs/lib/commands/stonith.py | 3 +-
|
||||
pcs/lib/resource_agent/__init__.py | 1 -
|
||||
pcs/lib/resource_agent/error.py | 14 --
|
||||
pcs/lib/resource_agent/facade.py | 37 ++-
|
||||
pcs/lib/resource_agent/xml.py | 15 +-
|
||||
.../tier0/common/reports/test_messages.py | 18 +-
|
||||
.../tier0/lib/resource_agent/test_facade.py | 47 ++++
|
||||
pcs_test/tier0/lib/resource_agent/test_xml.py | 226 ++++++++----------
|
||||
12 files changed, 201 insertions(+), 184 deletions(-)
|
||||
|
||||
diff --git a/pcs/common/reports/codes.py b/pcs/common/reports/codes.py
|
||||
index 3e0512d9..e8dee00f 100644
|
||||
--- a/pcs/common/reports/codes.py
|
||||
+++ b/pcs/common/reports/codes.py
|
||||
@@ -36,8 +36,8 @@ ADD_REMOVE_CANNOT_SPECIFY_ADJACENT_ITEM_WITHOUT_ITEMS_TO_ADD = M(
|
||||
"ADD_REMOVE_CANNOT_SPECIFY_ADJACENT_ITEM_WITHOUT_ITEMS_TO_ADD"
|
||||
)
|
||||
AGENT_GENERIC_ERROR = M("AGENT_GENERIC_ERROR")
|
||||
-AGENT_IMPLEMENTS_UNSUPPORTED_OCF_VERSION = M(
|
||||
- "AGENT_IMPLEMENTS_UNSUPPORTED_OCF_VERSION"
|
||||
+AGENT_IMPLEMENTS_UNSUPPORTED_OCF_VERSION_ASSUMED_VERSION = M(
|
||||
+ "AGENT_IMPLEMENTS_UNSUPPORTED_OCF_VERSION_ASSUMED_VERSION"
|
||||
)
|
||||
AGENT_NAME_GUESS_FOUND_MORE_THAN_ONE = M("AGENT_NAME_GUESS_FOUND_MORE_THAN_ONE")
|
||||
AGENT_NAME_GUESS_FOUND_NONE = M("AGENT_NAME_GUESS_FOUND_NONE")
|
||||
diff --git a/pcs/common/reports/messages.py b/pcs/common/reports/messages.py
|
||||
index 9d665e73..7df1e1eb 100644
|
||||
--- a/pcs/common/reports/messages.py
|
||||
+++ b/pcs/common/reports/messages.py
|
||||
@@ -3789,9 +3789,9 @@ class AgentNameGuessFoundNone(ReportItemMessage):
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
-class AgentImplementsUnsupportedOcfVersion(ReportItemMessage):
|
||||
+class AgentImplementsUnsupportedOcfVersionAssumedVersion(ReportItemMessage):
|
||||
"""
|
||||
- Specified agent implements OCF version not supported by pcs
|
||||
+ Specified agent implements OCF version not supported by pcs, assumed OCF 1.0
|
||||
|
||||
agent -- name of the agent
|
||||
ocf_version -- OCF version implemented by the agent
|
||||
@@ -3801,7 +3801,8 @@ class AgentImplementsUnsupportedOcfVersion(ReportItemMessage):
|
||||
agent: str
|
||||
ocf_version: str
|
||||
supported_versions: List[str]
|
||||
- _code = codes.AGENT_IMPLEMENTS_UNSUPPORTED_OCF_VERSION
|
||||
+ assumed_version: str
|
||||
+ _code = codes.AGENT_IMPLEMENTS_UNSUPPORTED_OCF_VERSION_ASSUMED_VERSION
|
||||
|
||||
@property
|
||||
def message(self) -> str:
|
||||
@@ -3809,9 +3810,9 @@ class AgentImplementsUnsupportedOcfVersion(ReportItemMessage):
|
||||
_is = format_plural(self.supported_versions, "is")
|
||||
_version_list = format_list(self.supported_versions)
|
||||
return (
|
||||
- f"Unable to process agent '{self.agent}' as it implements "
|
||||
- f"unsupported OCF version '{self.ocf_version}', supported "
|
||||
- f"{_version} {_is}: {_version_list}"
|
||||
+ f"Agent '{self.agent}' implements unsupported OCF version "
|
||||
+ f"'{self.ocf_version}', supported {_version} {_is}: "
|
||||
+ f"{_version_list}; assumed version '{self.assumed_version}'"
|
||||
)
|
||||
|
||||
|
||||
diff --git a/pcs/lib/commands/resource.py b/pcs/lib/commands/resource.py
|
||||
index 82ce73e0..c4b6252c 100644
|
||||
--- a/pcs/lib/commands/resource.py
|
||||
+++ b/pcs/lib/commands/resource.py
|
||||
@@ -84,7 +84,6 @@ from pcs.lib.resource_agent import (
|
||||
ResourceAgentName,
|
||||
split_resource_agent_name,
|
||||
UnableToGetAgentMetadata,
|
||||
- UnsupportedOcfVersion,
|
||||
)
|
||||
from pcs.lib.tools import get_tmp_cib
|
||||
from pcs.lib.validate import ValueTimeInterval
|
||||
@@ -162,7 +161,7 @@ def _get_agent_facade(
|
||||
else find_one_resource_agent_by_type(runner, report_processor, name)
|
||||
)
|
||||
return factory.facade_from_parsed_name(split_name)
|
||||
- except (UnableToGetAgentMetadata, UnsupportedOcfVersion) as e:
|
||||
+ except UnableToGetAgentMetadata as e:
|
||||
if allow_absent_agent:
|
||||
report_processor.report(
|
||||
resource_agent_error_to_report_item(
|
||||
diff --git a/pcs/lib/commands/resource_agent.py b/pcs/lib/commands/resource_agent.py
|
||||
index e6167b13..4a1831c0 100644
|
||||
--- a/pcs/lib/commands/resource_agent.py
|
||||
+++ b/pcs/lib/commands/resource_agent.py
|
||||
@@ -139,7 +139,9 @@ def _complete_agent_list(
|
||||
try:
|
||||
split_name = split_resource_agent_name(name)
|
||||
metadata = (
|
||||
- agent_factory.facade_from_parsed_name(split_name).metadata
|
||||
+ agent_factory.facade_from_parsed_name(
|
||||
+ split_name, report_warnings=False
|
||||
+ ).metadata
|
||||
if describe
|
||||
else name_to_void_metadata(split_name)
|
||||
)
|
||||
diff --git a/pcs/lib/commands/stonith.py b/pcs/lib/commands/stonith.py
|
||||
index 093f5be9..2aa299d7 100644
|
||||
--- a/pcs/lib/commands/stonith.py
|
||||
+++ b/pcs/lib/commands/stonith.py
|
||||
@@ -45,7 +45,6 @@ from pcs.lib.resource_agent import (
|
||||
ResourceAgentFacadeFactory,
|
||||
ResourceAgentName,
|
||||
UnableToGetAgentMetadata,
|
||||
- UnsupportedOcfVersion,
|
||||
)
|
||||
from pcs.lib.validate import validate_add_remove_items
|
||||
from pcs.lib.xml_tools import get_root
|
||||
@@ -62,7 +61,7 @@ def _get_agent_facade(
|
||||
raise InvalidResourceAgentName(name)
|
||||
full_name = ResourceAgentName("stonith", None, name)
|
||||
return factory.facade_from_parsed_name(full_name)
|
||||
- except (UnableToGetAgentMetadata, UnsupportedOcfVersion) as e:
|
||||
+ except UnableToGetAgentMetadata as e:
|
||||
if allow_absent_agent:
|
||||
report_processor.report(
|
||||
resource_agent_error_to_report_item(
|
||||
diff --git a/pcs/lib/resource_agent/__init__.py b/pcs/lib/resource_agent/__init__.py
|
||||
index 4548017f..c6086331 100644
|
||||
--- a/pcs/lib/resource_agent/__init__.py
|
||||
+++ b/pcs/lib/resource_agent/__init__.py
|
||||
@@ -10,7 +10,6 @@ from .error import (
|
||||
ResourceAgentError,
|
||||
resource_agent_error_to_report_item,
|
||||
UnableToGetAgentMetadata,
|
||||
- UnsupportedOcfVersion,
|
||||
)
|
||||
from .facade import ResourceAgentFacade, ResourceAgentFacadeFactory
|
||||
from .list import (
|
||||
diff --git a/pcs/lib/resource_agent/error.py b/pcs/lib/resource_agent/error.py
|
||||
index d4178333..f1dd7f3d 100644
|
||||
--- a/pcs/lib/resource_agent/error.py
|
||||
+++ b/pcs/lib/resource_agent/error.py
|
||||
@@ -2,8 +2,6 @@ from typing import Iterable
|
||||
|
||||
from pcs.common import reports
|
||||
|
||||
-from . import const
|
||||
-
|
||||
|
||||
class ResourceAgentError(Exception):
|
||||
def __init__(self, agent_name: str):
|
||||
@@ -37,12 +35,6 @@ class UnableToGetAgentMetadata(ResourceAgentError):
|
||||
self.message = message
|
||||
|
||||
|
||||
-class UnsupportedOcfVersion(ResourceAgentError):
|
||||
- def __init__(self, agent_name: str, ocf_version: str):
|
||||
- super().__init__(agent_name)
|
||||
- self.ocf_version = ocf_version
|
||||
-
|
||||
-
|
||||
def resource_agent_error_to_report_item(
|
||||
e: ResourceAgentError,
|
||||
severity: reports.ReportItemSeverity = reports.ReportItemSeverity.error(),
|
||||
@@ -69,10 +61,4 @@ def resource_agent_error_to_report_item(
|
||||
message = reports.messages.UnableToGetAgentMetadata(
|
||||
e.agent_name, e.message
|
||||
)
|
||||
- elif isinstance(e, UnsupportedOcfVersion):
|
||||
- message = reports.messages.AgentImplementsUnsupportedOcfVersion(
|
||||
- e.agent_name,
|
||||
- e.ocf_version,
|
||||
- sorted(const.SUPPORTED_OCF_VERSIONS),
|
||||
- )
|
||||
return reports.ReportItem(severity, message)
|
||||
diff --git a/pcs/lib/resource_agent/facade.py b/pcs/lib/resource_agent/facade.py
|
||||
index 4dbb59b8..dea59a1a 100644
|
||||
--- a/pcs/lib/resource_agent/facade.py
|
||||
+++ b/pcs/lib/resource_agent/facade.py
|
||||
@@ -188,18 +188,32 @@ class ResourceAgentFacadeFactory:
|
||||
self._fenced_metadata = None
|
||||
|
||||
def facade_from_parsed_name(
|
||||
- self, name: ResourceAgentName
|
||||
+ self, name: ResourceAgentName, report_warnings=True
|
||||
) -> ResourceAgentFacade:
|
||||
"""
|
||||
Create ResourceAgentFacade based on specified agent name
|
||||
|
||||
name -- agent name to get a facade for
|
||||
"""
|
||||
- return self._facade_from_metadata(
|
||||
- ocf_version_to_ocf_unified(
|
||||
- parse_metadata(name, load_metadata(self._runner, name))
|
||||
- )
|
||||
+ metadata, raw_ocf_version = parse_metadata(
|
||||
+ name,
|
||||
+ load_metadata(self._runner, name),
|
||||
)
|
||||
+ if (
|
||||
+ report_warnings
|
||||
+ and raw_ocf_version not in const.SUPPORTED_OCF_VERSIONS
|
||||
+ ):
|
||||
+ self._report_processor.report(
|
||||
+ reports.ReportItem.warning(
|
||||
+ reports.messages.AgentImplementsUnsupportedOcfVersionAssumedVersion(
|
||||
+ name.full_name,
|
||||
+ raw_ocf_version,
|
||||
+ sorted(const.SUPPORTED_OCF_VERSIONS),
|
||||
+ const.OCF_1_0,
|
||||
+ )
|
||||
+ )
|
||||
+ )
|
||||
+ return self._facade_from_metadata(ocf_version_to_ocf_unified(metadata))
|
||||
|
||||
def void_facade_from_parsed_name(
|
||||
self, name: ResourceAgentName
|
||||
@@ -232,15 +246,12 @@ class ResourceAgentFacadeFactory:
|
||||
const.FAKE_AGENT_STANDARD, None, const.PACEMAKER_FENCED
|
||||
)
|
||||
try:
|
||||
+ metadata, _ = parse_metadata(
|
||||
+ agent_name,
|
||||
+ load_fake_agent_metadata(self._runner, agent_name.type),
|
||||
+ )
|
||||
self._fenced_metadata = ocf_unified_to_pcs(
|
||||
- ocf_version_to_ocf_unified(
|
||||
- parse_metadata(
|
||||
- agent_name,
|
||||
- load_fake_agent_metadata(
|
||||
- self._runner, agent_name.type
|
||||
- ),
|
||||
- )
|
||||
- )
|
||||
+ ocf_version_to_ocf_unified(metadata)
|
||||
)
|
||||
except ResourceAgentError as e:
|
||||
# If pcs is unable to load fenced metadata, cache an empty
|
||||
diff --git a/pcs/lib/resource_agent/xml.py b/pcs/lib/resource_agent/xml.py
|
||||
index 82f8fbfa..1ba97216 100644
|
||||
--- a/pcs/lib/resource_agent/xml.py
|
||||
+++ b/pcs/lib/resource_agent/xml.py
|
||||
@@ -8,7 +8,7 @@ from pcs.common.tools import xml_fromstring
|
||||
from pcs.lib.external import CommandRunner
|
||||
|
||||
from . import const
|
||||
-from .error import UnableToGetAgentMetadata, UnsupportedOcfVersion
|
||||
+from .error import UnableToGetAgentMetadata
|
||||
from .types import (
|
||||
FakeAgentName,
|
||||
ResourceAgentActionOcf1_0,
|
||||
@@ -137,8 +137,11 @@ def load_fake_agent_metadata(
|
||||
|
||||
|
||||
def parse_metadata(
|
||||
- name: ResourceAgentName, metadata: _Element
|
||||
-) -> Union[ResourceAgentMetadataOcf1_0, ResourceAgentMetadataOcf1_1]:
|
||||
+ name: ResourceAgentName,
|
||||
+ metadata: _Element,
|
||||
+) -> Tuple[
|
||||
+ Union[ResourceAgentMetadataOcf1_0, ResourceAgentMetadataOcf1_1], str
|
||||
+]:
|
||||
"""
|
||||
Parse XML metadata to a dataclass
|
||||
|
||||
@@ -146,11 +149,9 @@ def parse_metadata(
|
||||
metadata -- metadata XML document
|
||||
"""
|
||||
ocf_version = _get_ocf_version(metadata)
|
||||
- if ocf_version == const.OCF_1_0:
|
||||
- return _parse_agent_1_0(name, metadata)
|
||||
if ocf_version == const.OCF_1_1:
|
||||
- return _parse_agent_1_1(name, metadata)
|
||||
- raise UnsupportedOcfVersion(name.full_name, ocf_version)
|
||||
+ return _parse_agent_1_1(name, metadata), ocf_version
|
||||
+ return _parse_agent_1_0(name, metadata), ocf_version
|
||||
|
||||
|
||||
def _parse_agent_1_0(
|
||||
diff --git a/pcs_test/tier0/common/reports/test_messages.py b/pcs_test/tier0/common/reports/test_messages.py
|
||||
index 4a7b4945..b885a9eb 100644
|
||||
--- a/pcs_test/tier0/common/reports/test_messages.py
|
||||
+++ b/pcs_test/tier0/common/reports/test_messages.py
|
||||
@@ -2833,22 +2833,22 @@ class AgentNameGuessFoundNone(NameBuildTest):
|
||||
)
|
||||
|
||||
|
||||
-class AgentImplementsUnsupportedOcfVersion(NameBuildTest):
|
||||
+class AgentImplementsUnsupportedOcfVersionAssumedVersion(NameBuildTest):
|
||||
def test_singular(self):
|
||||
self.assert_message_from_report(
|
||||
- "Unable to process agent 'agent-name' as it implements unsupported "
|
||||
- "OCF version 'ocf-2.3', supported version is: 'v1'",
|
||||
- reports.AgentImplementsUnsupportedOcfVersion(
|
||||
- "agent-name", "ocf-2.3", ["v1"]
|
||||
+ "Agent 'agent-name' implements unsupported OCF version 'ocf-2.3', "
|
||||
+ "supported version is: 'v1'; assumed version 'v1'",
|
||||
+ reports.AgentImplementsUnsupportedOcfVersionAssumedVersion(
|
||||
+ "agent-name", "ocf-2.3", ["v1"], "v1"
|
||||
),
|
||||
)
|
||||
|
||||
def test_plural(self):
|
||||
self.assert_message_from_report(
|
||||
- "Unable to process agent 'agent-name' as it implements unsupported "
|
||||
- "OCF version 'ocf-2.3', supported versions are: 'v1', 'v2', 'v3'",
|
||||
- reports.AgentImplementsUnsupportedOcfVersion(
|
||||
- "agent-name", "ocf-2.3", ["v1", "v2", "v3"]
|
||||
+ "Agent 'agent-name' implements unsupported OCF version 'ocf-2.3', "
|
||||
+ "supported versions are: 'v1', 'v2', 'v3'; assumed version 'v1'",
|
||||
+ reports.AgentImplementsUnsupportedOcfVersionAssumedVersion(
|
||||
+ "agent-name", "ocf-2.3", ["v1", "v2", "v3"], "v1"
|
||||
),
|
||||
)
|
||||
|
||||
diff --git a/pcs_test/tier0/lib/resource_agent/test_facade.py b/pcs_test/tier0/lib/resource_agent/test_facade.py
|
||||
index 654eb35e..f6a9899c 100644
|
||||
--- a/pcs_test/tier0/lib/resource_agent/test_facade.py
|
||||
+++ b/pcs_test/tier0/lib/resource_agent/test_facade.py
|
||||
@@ -92,6 +92,14 @@ class ResourceAgentFacadeFactory(TestCase):
|
||||
</parameters>
|
||||
</resource-agent>
|
||||
"""
|
||||
+ _fixture_agent_bad_version_xml = """
|
||||
+ <resource-agent name="agent">
|
||||
+ <version>0.1.2</version>
|
||||
+ <parameters>
|
||||
+ <parameter name="agent-param"/>
|
||||
+ </parameters>
|
||||
+ </resource-agent>
|
||||
+ """
|
||||
_fixture_fenced_xml = """
|
||||
<resource-agent name="pacemaker-fenced">
|
||||
<parameters>
|
||||
@@ -125,6 +133,45 @@ class ResourceAgentFacadeFactory(TestCase):
|
||||
self.assertEqual(facade.metadata.name, name)
|
||||
self.assertTrue(facade.metadata.agent_exists)
|
||||
|
||||
+ def test_facade_bad_ocf_version(self):
|
||||
+ name = ra.ResourceAgentName("service", None, "daemon")
|
||||
+ self.config.runner.pcmk.load_agent(
|
||||
+ agent_name="service:daemon",
|
||||
+ stdout=self._fixture_agent_bad_version_xml,
|
||||
+ )
|
||||
+
|
||||
+ env = self.env_assist.get_env()
|
||||
+ facade = ra.ResourceAgentFacadeFactory(
|
||||
+ env.cmd_runner(), env.report_processor
|
||||
+ ).facade_from_parsed_name(name)
|
||||
+ self.assertEqual(facade.metadata.name, name)
|
||||
+ self.assertTrue(facade.metadata.agent_exists)
|
||||
+ self.env_assist.assert_reports(
|
||||
+ [
|
||||
+ fixture.warn(
|
||||
+ reports.codes.AGENT_IMPLEMENTS_UNSUPPORTED_OCF_VERSION_ASSUMED_VERSION,
|
||||
+ agent=name.full_name,
|
||||
+ ocf_version="0.1.2",
|
||||
+ supported_versions=sorted(ra.const.SUPPORTED_OCF_VERSIONS),
|
||||
+ assumed_version=ra.const.OCF_1_0,
|
||||
+ )
|
||||
+ ]
|
||||
+ )
|
||||
+
|
||||
+ def test_facade_bad_ocf_version_disabled_warning(self):
|
||||
+ name = ra.ResourceAgentName("service", None, "daemon")
|
||||
+ self.config.runner.pcmk.load_agent(
|
||||
+ agent_name="service:daemon",
|
||||
+ stdout=self._fixture_agent_bad_version_xml,
|
||||
+ )
|
||||
+
|
||||
+ env = self.env_assist.get_env()
|
||||
+ facade = ra.ResourceAgentFacadeFactory(
|
||||
+ env.cmd_runner(), env.report_processor
|
||||
+ ).facade_from_parsed_name(name, report_warnings=False)
|
||||
+ self.assertEqual(facade.metadata.name, name)
|
||||
+ self.assertTrue(facade.metadata.agent_exists)
|
||||
+
|
||||
def test_facade_missing_agent(self):
|
||||
name = ra.ResourceAgentName("service", None, "daemon")
|
||||
self.config.runner.pcmk.load_agent(
|
||||
diff --git a/pcs_test/tier0/lib/resource_agent/test_xml.py b/pcs_test/tier0/lib/resource_agent/test_xml.py
|
||||
index c4176f32..26bbbb7d 100644
|
||||
--- a/pcs_test/tier0/lib/resource_agent/test_xml.py
|
||||
+++ b/pcs_test/tier0/lib/resource_agent/test_xml.py
|
||||
@@ -351,6 +351,7 @@ class LoadFakeAgentMetadata(TestCase):
|
||||
class ParseOcfToolsMixin:
|
||||
agent_name = ra.ResourceAgentName("ocf", "pacemaker", "Dummy")
|
||||
ocf_version = None
|
||||
+ parsed_ocf_version = None
|
||||
|
||||
def parse(self, xml, agent_name=None):
|
||||
agent_name = agent_name or self.agent_name
|
||||
@@ -383,19 +384,17 @@ class ParseOcfToolsMixin:
|
||||
version_el.text = ocf_version
|
||||
return etree_to_str(dom)
|
||||
|
||||
-
|
||||
-class ParseOcfGeneric(ParseOcfToolsMixin, TestCase):
|
||||
- def test_unsupported_ocf_version(self):
|
||||
- with self.assertRaises(ra.UnsupportedOcfVersion) as cm:
|
||||
- self.parse(self.xml("""<resource-agent/>""", ocf_version="1.2"))
|
||||
- self.assertEqual(cm.exception.agent_name, self.agent_name.full_name)
|
||||
- self.assertEqual(cm.exception.ocf_version, "1.2")
|
||||
+ def assert_parse_result(self, xml, metadata):
|
||||
+ self.assertEqual(
|
||||
+ self.parse(xml),
|
||||
+ (metadata, self.parsed_ocf_version or self.ocf_version),
|
||||
+ )
|
||||
|
||||
|
||||
class ParseOcf10BaseMixin(ParseOcfToolsMixin):
|
||||
def test_empty_agent(self):
|
||||
- self.assertEqual(
|
||||
- self.parse(self.xml("""<resource-agent/>""")),
|
||||
+ self.assert_parse_result(
|
||||
+ self.xml("""<resource-agent/>"""),
|
||||
ResourceAgentMetadataOcf1_0(
|
||||
self.agent_name,
|
||||
shortdesc=None,
|
||||
@@ -406,16 +405,14 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
|
||||
)
|
||||
|
||||
def test_desc_element(self):
|
||||
- self.assertEqual(
|
||||
- self.parse(
|
||||
- self.xml(
|
||||
- """
|
||||
+ self.assert_parse_result(
|
||||
+ self.xml(
|
||||
+ """
|
||||
<resource-agent>
|
||||
<shortdesc>This is a shortdesc</shortdesc>
|
||||
<longdesc>This is a longdesc</longdesc>
|
||||
</resource-agent>
|
||||
"""
|
||||
- )
|
||||
),
|
||||
ResourceAgentMetadataOcf1_0(
|
||||
self.agent_name,
|
||||
@@ -427,16 +424,14 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
|
||||
)
|
||||
|
||||
def test_desc_element_empty(self):
|
||||
- self.assertEqual(
|
||||
- self.parse(
|
||||
- self.xml(
|
||||
- """
|
||||
+ self.assert_parse_result(
|
||||
+ self.xml(
|
||||
+ """
|
||||
<resource-agent>
|
||||
<longdesc/>
|
||||
<shortdesc/>
|
||||
</resource-agent>
|
||||
"""
|
||||
- )
|
||||
),
|
||||
ResourceAgentMetadataOcf1_0(
|
||||
self.agent_name,
|
||||
@@ -448,15 +443,13 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
|
||||
)
|
||||
|
||||
def test_desc_attribute(self):
|
||||
- self.assertEqual(
|
||||
- self.parse(
|
||||
- self.xml(
|
||||
- """
|
||||
+ self.assert_parse_result(
|
||||
+ self.xml(
|
||||
+ """
|
||||
<resource-agent shortdesc="This is a shortdesc">
|
||||
<longdesc></longdesc>
|
||||
</resource-agent>
|
||||
"""
|
||||
- )
|
||||
),
|
||||
ResourceAgentMetadataOcf1_0(
|
||||
self.agent_name,
|
||||
@@ -468,13 +461,11 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
|
||||
)
|
||||
|
||||
def test_desc_attribute_empty(self):
|
||||
- self.assertEqual(
|
||||
- self.parse(
|
||||
- self.xml(
|
||||
- """
|
||||
+ self.assert_parse_result(
|
||||
+ self.xml(
|
||||
+ """
|
||||
<resource-agent shortdesc=""/>
|
||||
"""
|
||||
- )
|
||||
),
|
||||
ResourceAgentMetadataOcf1_0(
|
||||
self.agent_name,
|
||||
@@ -486,15 +477,13 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
|
||||
)
|
||||
|
||||
def test_desc_element_and_attribute(self):
|
||||
- self.assertEqual(
|
||||
- self.parse(
|
||||
- self.xml(
|
||||
- """
|
||||
+ self.assert_parse_result(
|
||||
+ self.xml(
|
||||
+ """
|
||||
<resource-agent shortdesc="shortdesc attribute">
|
||||
<shortdesc>shortdesc element</shortdesc>
|
||||
</resource-agent>
|
||||
"""
|
||||
- )
|
||||
),
|
||||
ResourceAgentMetadataOcf1_0(
|
||||
self.agent_name,
|
||||
@@ -506,15 +495,13 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
|
||||
)
|
||||
|
||||
def test_desc_element_empty_and_attribute(self):
|
||||
- self.assertEqual(
|
||||
- self.parse(
|
||||
- self.xml(
|
||||
- """
|
||||
+ self.assert_parse_result(
|
||||
+ self.xml(
|
||||
+ """
|
||||
<resource-agent shortdesc="shortdesc attribute">
|
||||
<shortdesc></shortdesc>
|
||||
</resource-agent>
|
||||
"""
|
||||
- )
|
||||
),
|
||||
ResourceAgentMetadataOcf1_0(
|
||||
self.agent_name,
|
||||
@@ -526,15 +513,13 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
|
||||
)
|
||||
|
||||
def test_desc_element_empty_and_attribute_empty(self):
|
||||
- self.assertEqual(
|
||||
- self.parse(
|
||||
- self.xml(
|
||||
- """
|
||||
+ self.assert_parse_result(
|
||||
+ self.xml(
|
||||
+ """
|
||||
<resource-agent shortdesc="">
|
||||
<shortdesc></shortdesc>
|
||||
</resource-agent>
|
||||
"""
|
||||
- )
|
||||
),
|
||||
ResourceAgentMetadataOcf1_0(
|
||||
self.agent_name,
|
||||
@@ -546,15 +531,13 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
|
||||
)
|
||||
|
||||
def test_parameters_empty_list(self):
|
||||
- self.assertEqual(
|
||||
- self.parse(
|
||||
- self.xml(
|
||||
- """
|
||||
+ self.assert_parse_result(
|
||||
+ self.xml(
|
||||
+ """
|
||||
<resource-agent>
|
||||
<parameters/>
|
||||
</resource-agent>
|
||||
"""
|
||||
- )
|
||||
),
|
||||
ResourceAgentMetadataOcf1_0(
|
||||
self.agent_name,
|
||||
@@ -581,17 +564,15 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
|
||||
)
|
||||
|
||||
def test_parameters_minimal(self):
|
||||
- self.assertEqual(
|
||||
- self.parse(
|
||||
- self.xml(
|
||||
- """
|
||||
+ self.assert_parse_result(
|
||||
+ self.xml(
|
||||
+ """
|
||||
<resource-agent>
|
||||
<parameters>
|
||||
<parameter name="a_parameter"/>
|
||||
</parameters>
|
||||
</resource-agent>
|
||||
"""
|
||||
- )
|
||||
),
|
||||
ResourceAgentMetadataOcf1_0(
|
||||
self.agent_name,
|
||||
@@ -616,10 +597,9 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
|
||||
)
|
||||
|
||||
def test_parameters_all_settings(self):
|
||||
- self.assertEqual(
|
||||
- self.parse(
|
||||
- self.xml(
|
||||
- """
|
||||
+ self.assert_parse_result(
|
||||
+ self.xml(
|
||||
+ """
|
||||
<resource-agent>
|
||||
<parameters>
|
||||
<parameter name="a_parameter" required="1"
|
||||
@@ -632,7 +612,6 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
|
||||
</parameters>
|
||||
</resource-agent>
|
||||
"""
|
||||
- )
|
||||
),
|
||||
ResourceAgentMetadataOcf1_0(
|
||||
self.agent_name,
|
||||
@@ -657,10 +636,9 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
|
||||
)
|
||||
|
||||
def test_parameters_content(self):
|
||||
- self.assertEqual(
|
||||
- self.parse(
|
||||
- self.xml(
|
||||
- """
|
||||
+ self.assert_parse_result(
|
||||
+ self.xml(
|
||||
+ """
|
||||
<resource-agent>
|
||||
<parameters>
|
||||
<parameter name="with_type">
|
||||
@@ -676,7 +654,6 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
|
||||
</parameters>
|
||||
</resource-agent>
|
||||
"""
|
||||
- )
|
||||
),
|
||||
ResourceAgentMetadataOcf1_0(
|
||||
self.agent_name,
|
||||
@@ -713,15 +690,13 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
|
||||
)
|
||||
|
||||
def test_actions_empty_list(self):
|
||||
- self.assertEqual(
|
||||
- self.parse(
|
||||
- self.xml(
|
||||
- """
|
||||
+ self.assert_parse_result(
|
||||
+ self.xml(
|
||||
+ """
|
||||
<resource-agent>
|
||||
<actions/>
|
||||
</resource-agent>
|
||||
"""
|
||||
- )
|
||||
),
|
||||
ResourceAgentMetadataOcf1_0(
|
||||
self.agent_name,
|
||||
@@ -748,10 +723,9 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
|
||||
)
|
||||
|
||||
def test_actions_multiple(self):
|
||||
- self.assertEqual(
|
||||
- self.parse(
|
||||
- self.xml(
|
||||
- """
|
||||
+ self.assert_parse_result(
|
||||
+ self.xml(
|
||||
+ """
|
||||
<resource-agent>
|
||||
<actions>
|
||||
<action name="minimal"/>
|
||||
@@ -764,7 +738,6 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
|
||||
</actions>
|
||||
</resource-agent>
|
||||
"""
|
||||
- )
|
||||
),
|
||||
ResourceAgentMetadataOcf1_0(
|
||||
self.agent_name,
|
||||
@@ -808,7 +781,26 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
|
||||
|
||||
|
||||
class ParseOcf10NoVersion(ParseOcf10BaseMixin, TestCase):
|
||||
- pass
|
||||
+ parsed_ocf_version = "1.0"
|
||||
+
|
||||
+
|
||||
+class ParseOcf10UnsupportedVersion(ParseOcf10BaseMixin, TestCase):
|
||||
+ ocf_version = "0.1.2"
|
||||
+
|
||||
+ # These tests test that pcs raises an error if an agent doesn't conform to
|
||||
+ # OCF schema. There is, however, no validation against OCF schema for
|
||||
+ # agents with unsupported OCF version. That means no error message, pcs
|
||||
+ # tries to process the agent and crashes. However bad that sounds, it's
|
||||
+ # indended as that's how pcs behaved before OCF 1.1 was implemented.
|
||||
+ # There's therefore no point in running these tests.
|
||||
+
|
||||
+ def test_parameters_empty_parameter(self):
|
||||
+ # parameters must have at least 'name' attribute
|
||||
+ pass
|
||||
+
|
||||
+ def test_actions_empty_action(self):
|
||||
+ # actions must have at least 'name' attribute
|
||||
+ pass
|
||||
|
||||
|
||||
class ParseOcf10ExplicitVersion(ParseOcf10BaseMixin, TestCase):
|
||||
@@ -819,8 +811,8 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
|
||||
ocf_version = "1.1"
|
||||
|
||||
def test_empty_agent(self):
|
||||
- self.assertEqual(
|
||||
- self.parse(self.xml("""<resource-agent/>""")),
|
||||
+ self.assert_parse_result(
|
||||
+ self.xml("""<resource-agent/>"""),
|
||||
ResourceAgentMetadataOcf1_1(
|
||||
self.agent_name,
|
||||
shortdesc=None,
|
||||
@@ -831,16 +823,14 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
|
||||
)
|
||||
|
||||
def test_desc_element(self):
|
||||
- self.assertEqual(
|
||||
- self.parse(
|
||||
- self.xml(
|
||||
- """
|
||||
+ self.assert_parse_result(
|
||||
+ self.xml(
|
||||
+ """
|
||||
<resource-agent>
|
||||
<shortdesc>This is a shortdesc</shortdesc>
|
||||
<longdesc>This is a longdesc</longdesc>
|
||||
</resource-agent>
|
||||
"""
|
||||
- )
|
||||
),
|
||||
ResourceAgentMetadataOcf1_1(
|
||||
self.agent_name,
|
||||
@@ -852,16 +842,14 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
|
||||
)
|
||||
|
||||
def test_desc_element_empty(self):
|
||||
- self.assertEqual(
|
||||
- self.parse(
|
||||
- self.xml(
|
||||
- """
|
||||
+ self.assert_parse_result(
|
||||
+ self.xml(
|
||||
+ """
|
||||
<resource-agent>
|
||||
<longdesc/>
|
||||
<shortdesc/>
|
||||
</resource-agent>
|
||||
"""
|
||||
- )
|
||||
),
|
||||
ResourceAgentMetadataOcf1_1(
|
||||
self.agent_name,
|
||||
@@ -873,15 +861,13 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
|
||||
)
|
||||
|
||||
def test_parameters_empty_list(self):
|
||||
- self.assertEqual(
|
||||
- self.parse(
|
||||
- self.xml(
|
||||
- """
|
||||
+ self.assert_parse_result(
|
||||
+ self.xml(
|
||||
+ """
|
||||
<resource-agent>
|
||||
<parameters/>
|
||||
</resource-agent>
|
||||
"""
|
||||
- )
|
||||
),
|
||||
ResourceAgentMetadataOcf1_1(
|
||||
self.agent_name,
|
||||
@@ -908,17 +894,15 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
|
||||
)
|
||||
|
||||
def test_parameters_minimal(self):
|
||||
- self.assertEqual(
|
||||
- self.parse(
|
||||
- self.xml(
|
||||
- """
|
||||
+ self.assert_parse_result(
|
||||
+ self.xml(
|
||||
+ """
|
||||
<resource-agent>
|
||||
<parameters>
|
||||
<parameter name="a_parameter"/>
|
||||
</parameters>
|
||||
</resource-agent>
|
||||
"""
|
||||
- )
|
||||
),
|
||||
ResourceAgentMetadataOcf1_1(
|
||||
self.agent_name,
|
||||
@@ -945,10 +929,9 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
|
||||
)
|
||||
|
||||
def test_parameters_deprecated_minimal(self):
|
||||
- self.assertEqual(
|
||||
- self.parse(
|
||||
- self.xml(
|
||||
- """
|
||||
+ self.assert_parse_result(
|
||||
+ self.xml(
|
||||
+ """
|
||||
<resource-agent>
|
||||
<parameters>
|
||||
<parameter name="a_parameter">
|
||||
@@ -957,7 +940,6 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
|
||||
</parameters>
|
||||
</resource-agent>
|
||||
"""
|
||||
- )
|
||||
),
|
||||
ResourceAgentMetadataOcf1_1(
|
||||
self.agent_name,
|
||||
@@ -984,10 +966,9 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
|
||||
)
|
||||
|
||||
def test_parameters_deprecated_replaced_with(self):
|
||||
- self.assertEqual(
|
||||
- self.parse(
|
||||
- self.xml(
|
||||
- """
|
||||
+ self.assert_parse_result(
|
||||
+ self.xml(
|
||||
+ """
|
||||
<resource-agent>
|
||||
<parameters>
|
||||
<parameter name="a_parameter">
|
||||
@@ -999,7 +980,6 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
|
||||
</parameters>
|
||||
</resource-agent>
|
||||
"""
|
||||
- )
|
||||
),
|
||||
ResourceAgentMetadataOcf1_1(
|
||||
self.agent_name,
|
||||
@@ -1026,10 +1006,9 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
|
||||
)
|
||||
|
||||
def test_parameters_all_settings(self):
|
||||
- self.assertEqual(
|
||||
- self.parse(
|
||||
- self.xml(
|
||||
- """
|
||||
+ self.assert_parse_result(
|
||||
+ self.xml(
|
||||
+ """
|
||||
<resource-agent>
|
||||
<parameters>
|
||||
<parameter name="a_parameter"
|
||||
@@ -1048,7 +1027,6 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
|
||||
</parameters>
|
||||
</resource-agent>
|
||||
"""
|
||||
- )
|
||||
),
|
||||
ResourceAgentMetadataOcf1_1(
|
||||
self.agent_name,
|
||||
@@ -1075,10 +1053,9 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
|
||||
)
|
||||
|
||||
def test_parameters_content(self):
|
||||
- self.assertEqual(
|
||||
- self.parse(
|
||||
- self.xml(
|
||||
- """
|
||||
+ self.assert_parse_result(
|
||||
+ self.xml(
|
||||
+ """
|
||||
<resource-agent>
|
||||
<parameters>
|
||||
<parameter name="with_type">
|
||||
@@ -1094,7 +1071,6 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
|
||||
</parameters>
|
||||
</resource-agent>
|
||||
"""
|
||||
- )
|
||||
),
|
||||
ResourceAgentMetadataOcf1_1(
|
||||
self.agent_name,
|
||||
@@ -1135,15 +1111,13 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
|
||||
)
|
||||
|
||||
def test_actions_empty_list(self):
|
||||
- self.assertEqual(
|
||||
- self.parse(
|
||||
- self.xml(
|
||||
- """
|
||||
+ self.assert_parse_result(
|
||||
+ self.xml(
|
||||
+ """
|
||||
<resource-agent>
|
||||
<actions/>
|
||||
</resource-agent>
|
||||
"""
|
||||
- )
|
||||
),
|
||||
ResourceAgentMetadataOcf1_1(
|
||||
self.agent_name,
|
||||
@@ -1170,10 +1144,9 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
|
||||
)
|
||||
|
||||
def test_actions_multiple(self):
|
||||
- self.assertEqual(
|
||||
- self.parse(
|
||||
- self.xml(
|
||||
- """
|
||||
+ self.assert_parse_result(
|
||||
+ self.xml(
|
||||
+ """
|
||||
<resource-agent>
|
||||
<actions>
|
||||
<action name="minimal"/>
|
||||
@@ -1186,7 +1159,6 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
|
||||
</actions>
|
||||
</resource-agent>
|
||||
"""
|
||||
- )
|
||||
),
|
||||
ResourceAgentMetadataOcf1_1(
|
||||
self.agent_name,
|
||||
--
|
||||
2.34.1
|
||||
|
587
SOURCES/bz2050274-02-relax-OCF-1.0-parser.patch
Normal file
587
SOURCES/bz2050274-02-relax-OCF-1.0-parser.patch
Normal file
@ -0,0 +1,587 @@
|
||||
From 65b30a04a234449cb4aa65606d47bf1d673592a4 Mon Sep 17 00:00:00 2001
|
||||
From: Tomas Jelinek <tojeline@redhat.com>
|
||||
Date: Wed, 9 Feb 2022 11:16:49 +0100
|
||||
Subject: [PATCH 2/3] relax OCF 1.0 parser
|
||||
|
||||
---
|
||||
pcs/lib/resource_agent/facade.py | 50 ++++--
|
||||
pcs/lib/resource_agent/ocf_transform.py | 51 +++++-
|
||||
pcs/lib/resource_agent/xml.py | 8 +-
|
||||
.../tier0/lib/resource_agent/test_facade.py | 44 +++++
|
||||
.../lib/resource_agent/test_ocf_transform.py | 48 +++++-
|
||||
pcs_test/tier0/lib/resource_agent/test_xml.py | 155 ++++++++++--------
|
||||
6 files changed, 256 insertions(+), 100 deletions(-)
|
||||
|
||||
diff --git a/pcs/lib/resource_agent/facade.py b/pcs/lib/resource_agent/facade.py
|
||||
index dea59a1a..8a65eb1c 100644
|
||||
--- a/pcs/lib/resource_agent/facade.py
|
||||
+++ b/pcs/lib/resource_agent/facade.py
|
||||
@@ -2,12 +2,19 @@ from collections import defaultdict
|
||||
from dataclasses import replace as dc_replace
|
||||
from typing import Dict, Iterable, List, Optional, Set
|
||||
|
||||
+from lxml import etree
|
||||
+
|
||||
+from pcs import settings
|
||||
from pcs.common import reports
|
||||
from pcs.lib import validate
|
||||
from pcs.lib.external import CommandRunner
|
||||
|
||||
from . import const
|
||||
-from .error import ResourceAgentError, resource_agent_error_to_report_item
|
||||
+from .error import (
|
||||
+ ResourceAgentError,
|
||||
+ resource_agent_error_to_report_item,
|
||||
+ UnableToGetAgentMetadata,
|
||||
+)
|
||||
from .name import name_to_void_metadata
|
||||
from .ocf_transform import ocf_version_to_ocf_unified
|
||||
from .pcs_transform import get_additional_trace_parameters, ocf_unified_to_pcs
|
||||
@@ -195,24 +202,33 @@ class ResourceAgentFacadeFactory:
|
||||
|
||||
name -- agent name to get a facade for
|
||||
"""
|
||||
- metadata, raw_ocf_version = parse_metadata(
|
||||
- name,
|
||||
- load_metadata(self._runner, name),
|
||||
- )
|
||||
- if (
|
||||
- report_warnings
|
||||
- and raw_ocf_version not in const.SUPPORTED_OCF_VERSIONS
|
||||
- ):
|
||||
- self._report_processor.report(
|
||||
- reports.ReportItem.warning(
|
||||
- reports.messages.AgentImplementsUnsupportedOcfVersionAssumedVersion(
|
||||
- name.full_name,
|
||||
- raw_ocf_version,
|
||||
- sorted(const.SUPPORTED_OCF_VERSIONS),
|
||||
- const.OCF_1_0,
|
||||
+ dom_metadata = load_metadata(self._runner, name)
|
||||
+ metadata, raw_ocf_version = parse_metadata(name, dom_metadata)
|
||||
+ if report_warnings:
|
||||
+ if raw_ocf_version not in const.SUPPORTED_OCF_VERSIONS:
|
||||
+ self._report_processor.report(
|
||||
+ reports.ReportItem.warning(
|
||||
+ reports.messages.AgentImplementsUnsupportedOcfVersionAssumedVersion(
|
||||
+ name.full_name,
|
||||
+ raw_ocf_version,
|
||||
+ sorted(const.SUPPORTED_OCF_VERSIONS),
|
||||
+ const.OCF_1_0,
|
||||
+ )
|
||||
)
|
||||
)
|
||||
- )
|
||||
+ if raw_ocf_version != const.OCF_1_1:
|
||||
+ try:
|
||||
+ etree.RelaxNG(
|
||||
+ file=settings.path.ocf_1_0_schema
|
||||
+ ).assertValid(dom_metadata)
|
||||
+ except etree.DocumentInvalid as e:
|
||||
+ self._report_processor.report(
|
||||
+ resource_agent_error_to_report_item(
|
||||
+ UnableToGetAgentMetadata(name.full_name, str(e)),
|
||||
+ severity=reports.ReportItemSeverity.warning(),
|
||||
+ is_stonith=name.is_stonith,
|
||||
+ )
|
||||
+ )
|
||||
return self._facade_from_metadata(ocf_version_to_ocf_unified(metadata))
|
||||
|
||||
def void_facade_from_parsed_name(
|
||||
diff --git a/pcs/lib/resource_agent/ocf_transform.py b/pcs/lib/resource_agent/ocf_transform.py
|
||||
index e841b55e..7e6a14ad 100644
|
||||
--- a/pcs/lib/resource_agent/ocf_transform.py
|
||||
+++ b/pcs/lib/resource_agent/ocf_transform.py
|
||||
@@ -67,20 +67,42 @@ def _ocf_1_1_to_ocf_unified(
|
||||
longdesc=metadata.longdesc,
|
||||
parameters=_ocf_1_1_parameter_list_to_ocf_unified(metadata.parameters),
|
||||
# OCF 1.1 actions are the same as in OCF 1.0
|
||||
- actions=_ocf_1_0_action_list_to_ocf_unified(metadata.actions),
|
||||
+ actions=_ocf_1_1_action_list_to_ocf_unified(metadata.actions),
|
||||
)
|
||||
|
||||
|
||||
def _ocf_1_0_action_list_to_ocf_unified(
|
||||
- action_list: Iterable[
|
||||
- Union[ResourceAgentActionOcf1_0, ResourceAgentActionOcf1_1]
|
||||
- ],
|
||||
+ action_list: Iterable[ResourceAgentActionOcf1_0],
|
||||
) -> List[ResourceAgentAction]:
|
||||
"""
|
||||
Transform OCF 1.0 actions to a universal format
|
||||
|
||||
action_list -- actions according OCF 1.0
|
||||
"""
|
||||
+ return [
|
||||
+ ResourceAgentAction(
|
||||
+ name=action.name,
|
||||
+ timeout=action.timeout,
|
||||
+ interval=action.interval,
|
||||
+ role=action.role,
|
||||
+ start_delay=action.start_delay,
|
||||
+ depth=action.depth,
|
||||
+ automatic=_bool_value_legacy(action.automatic),
|
||||
+ on_target=_bool_value_legacy(action.on_target),
|
||||
+ )
|
||||
+ for action in action_list
|
||||
+ if action.name
|
||||
+ ]
|
||||
+
|
||||
+
|
||||
+def _ocf_1_1_action_list_to_ocf_unified(
|
||||
+ action_list: Iterable[ResourceAgentActionOcf1_1],
|
||||
+) -> List[ResourceAgentAction]:
|
||||
+ """
|
||||
+ Transform OCF 1.1 actions to a universal format
|
||||
+
|
||||
+ action_list -- actions according OCF 1.1
|
||||
+ """
|
||||
return [
|
||||
ResourceAgentAction(
|
||||
name=action.name,
|
||||
@@ -111,6 +133,8 @@ def _ocf_1_0_parameter_list_to_ocf_unified(
|
||||
|
||||
result = []
|
||||
for parameter in parameter_list:
|
||||
+ if not parameter.name:
|
||||
+ continue
|
||||
result.append(
|
||||
ResourceAgentParameter(
|
||||
name=parameter.name,
|
||||
@@ -119,17 +143,17 @@ def _ocf_1_0_parameter_list_to_ocf_unified(
|
||||
type=parameter.type,
|
||||
default=parameter.default,
|
||||
enum_values=parameter.enum_values,
|
||||
- required=_bool_value(parameter.required),
|
||||
+ required=_bool_value_legacy(parameter.required),
|
||||
advanced=False,
|
||||
- deprecated=_bool_value(parameter.deprecated),
|
||||
+ deprecated=_bool_value_legacy(parameter.deprecated),
|
||||
deprecated_by=sorted(deprecated_by_dict[parameter.name]),
|
||||
deprecated_desc=None,
|
||||
unique_group=(
|
||||
f"{const.DEFAULT_UNIQUE_GROUP_PREFIX}{parameter.name}"
|
||||
- if _bool_value(parameter.unique)
|
||||
+ if _bool_value_legacy(parameter.unique)
|
||||
else None
|
||||
),
|
||||
- reloadable=_bool_value(parameter.unique),
|
||||
+ reloadable=_bool_value_legacy(parameter.unique),
|
||||
)
|
||||
)
|
||||
return result
|
||||
@@ -170,3 +194,14 @@ def _bool_value(value: Optional[str]) -> bool:
|
||||
value -- raw bool value
|
||||
"""
|
||||
return value == "1"
|
||||
+
|
||||
+
|
||||
+def _bool_value_legacy(value: Optional[str]) -> bool:
|
||||
+ """
|
||||
+ Transform raw bool value from metadata to bool type in backward compatible way
|
||||
+
|
||||
+ value -- raw bool value
|
||||
+ """
|
||||
+ return (
|
||||
+ False if not value else value.lower() in {"true", "on", "yes", "y", "1"}
|
||||
+ )
|
||||
diff --git a/pcs/lib/resource_agent/xml.py b/pcs/lib/resource_agent/xml.py
|
||||
index 1ba97216..0fc70527 100644
|
||||
--- a/pcs/lib/resource_agent/xml.py
|
||||
+++ b/pcs/lib/resource_agent/xml.py
|
||||
@@ -94,9 +94,7 @@ def _metadata_xml_to_dom(metadata: str) -> _Element:
|
||||
"""
|
||||
dom = xml_fromstring(metadata)
|
||||
ocf_version = _get_ocf_version(dom)
|
||||
- if ocf_version == const.OCF_1_0:
|
||||
- etree.RelaxNG(file=settings.path.ocf_1_0_schema).assertValid(dom)
|
||||
- elif ocf_version == const.OCF_1_1:
|
||||
+ if ocf_version == const.OCF_1_1:
|
||||
etree.RelaxNG(file=settings.path.ocf_1_1_schema).assertValid(dom)
|
||||
return dom
|
||||
|
||||
@@ -230,7 +228,7 @@ def _parse_parameters_1_0(
|
||||
)
|
||||
result.append(
|
||||
ResourceAgentParameterOcf1_0(
|
||||
- name=str(parameter_el.attrib["name"]),
|
||||
+ name=str(parameter_el.get("name", "")),
|
||||
shortdesc=_get_shortdesc(parameter_el),
|
||||
longdesc=_get_longdesc(parameter_el),
|
||||
type=value_type,
|
||||
@@ -286,7 +284,7 @@ def _parse_parameters_1_1(
|
||||
def _parse_actions_1_0(element: _Element) -> List[ResourceAgentActionOcf1_0]:
|
||||
return [
|
||||
ResourceAgentActionOcf1_0(
|
||||
- name=str(action.attrib["name"]),
|
||||
+ name=str(action.get("name", "")),
|
||||
timeout=action.get("timeout"),
|
||||
interval=action.get("interval"),
|
||||
role=action.get("role"),
|
||||
diff --git a/pcs_test/tier0/lib/resource_agent/test_facade.py b/pcs_test/tier0/lib/resource_agent/test_facade.py
|
||||
index f6a9899c..313dfa2b 100644
|
||||
--- a/pcs_test/tier0/lib/resource_agent/test_facade.py
|
||||
+++ b/pcs_test/tier0/lib/resource_agent/test_facade.py
|
||||
@@ -100,6 +100,13 @@ class ResourceAgentFacadeFactory(TestCase):
|
||||
</parameters>
|
||||
</resource-agent>
|
||||
"""
|
||||
+ _fixture_agent_not_valid_xml = """
|
||||
+ <resource-agent name="agent">
|
||||
+ <parameters>
|
||||
+ <parameter label="something wrong"/>
|
||||
+ </parameters>
|
||||
+ </resource-agent>
|
||||
+ """
|
||||
_fixture_fenced_xml = """
|
||||
<resource-agent name="pacemaker-fenced">
|
||||
<parameters>
|
||||
@@ -172,6 +179,43 @@ class ResourceAgentFacadeFactory(TestCase):
|
||||
self.assertEqual(facade.metadata.name, name)
|
||||
self.assertTrue(facade.metadata.agent_exists)
|
||||
|
||||
+ def test_facade_ocf_1_0_not_valid(self):
|
||||
+ name = ra.ResourceAgentName("service", None, "daemon")
|
||||
+ self.config.runner.pcmk.load_agent(
|
||||
+ agent_name="service:daemon",
|
||||
+ stdout=self._fixture_agent_not_valid_xml,
|
||||
+ )
|
||||
+
|
||||
+ env = self.env_assist.get_env()
|
||||
+ facade = ra.ResourceAgentFacadeFactory(
|
||||
+ env.cmd_runner(), env.report_processor
|
||||
+ ).facade_from_parsed_name(name)
|
||||
+ self.assertEqual(facade.metadata.name, name)
|
||||
+ self.assertTrue(facade.metadata.agent_exists)
|
||||
+ self.env_assist.assert_reports(
|
||||
+ [
|
||||
+ fixture.warn(
|
||||
+ reports.codes.UNABLE_TO_GET_AGENT_METADATA,
|
||||
+ agent=name.full_name,
|
||||
+ reason="Element parameter failed to validate attributes, line 3",
|
||||
+ )
|
||||
+ ]
|
||||
+ )
|
||||
+
|
||||
+ def test_facade_ocf_1_0_not_valid_disabled_warning(self):
|
||||
+ name = ra.ResourceAgentName("service", None, "daemon")
|
||||
+ self.config.runner.pcmk.load_agent(
|
||||
+ agent_name="service:daemon",
|
||||
+ stdout=self._fixture_agent_not_valid_xml,
|
||||
+ )
|
||||
+
|
||||
+ env = self.env_assist.get_env()
|
||||
+ facade = ra.ResourceAgentFacadeFactory(
|
||||
+ env.cmd_runner(), env.report_processor
|
||||
+ ).facade_from_parsed_name(name, report_warnings=False)
|
||||
+ self.assertEqual(facade.metadata.name, name)
|
||||
+ self.assertTrue(facade.metadata.agent_exists)
|
||||
+
|
||||
def test_facade_missing_agent(self):
|
||||
name = ra.ResourceAgentName("service", None, "daemon")
|
||||
self.config.runner.pcmk.load_agent(
|
||||
diff --git a/pcs_test/tier0/lib/resource_agent/test_ocf_transform.py b/pcs_test/tier0/lib/resource_agent/test_ocf_transform.py
|
||||
index 9e41b6af..d0de86e5 100644
|
||||
--- a/pcs_test/tier0/lib/resource_agent/test_ocf_transform.py
|
||||
+++ b/pcs_test/tier0/lib/resource_agent/test_ocf_transform.py
|
||||
@@ -66,6 +66,18 @@ class OcfVersionToOcfUnified(TestCase):
|
||||
obsoletes=None,
|
||||
unique=None,
|
||||
),
|
||||
+ ra.types.ResourceAgentParameterOcf1_0(
|
||||
+ name="",
|
||||
+ shortdesc="Parameters with no name are ignored",
|
||||
+ longdesc=None,
|
||||
+ type="string",
|
||||
+ default=None,
|
||||
+ enum_values=None,
|
||||
+ required=None,
|
||||
+ deprecated=None,
|
||||
+ obsoletes=None,
|
||||
+ unique=None,
|
||||
+ ),
|
||||
ra.types.ResourceAgentParameterOcf1_0(
|
||||
name="param_2",
|
||||
shortdesc="param_2 shortdesc",
|
||||
@@ -109,10 +121,10 @@ class OcfVersionToOcfUnified(TestCase):
|
||||
type="string",
|
||||
default=None,
|
||||
enum_values=None,
|
||||
- required="1",
|
||||
- deprecated="1",
|
||||
+ required="yeS",
|
||||
+ deprecated="True",
|
||||
obsoletes="param_4",
|
||||
- unique="1",
|
||||
+ unique="on",
|
||||
),
|
||||
ra.types.ResourceAgentParameterOcf1_0(
|
||||
name="param_6",
|
||||
@@ -138,6 +150,16 @@ class OcfVersionToOcfUnified(TestCase):
|
||||
automatic=None,
|
||||
on_target=None,
|
||||
),
|
||||
+ ra.types.ResourceAgentActionOcf1_0(
|
||||
+ name="",
|
||||
+ timeout=None,
|
||||
+ interval=None,
|
||||
+ role=None,
|
||||
+ start_delay=None,
|
||||
+ depth=None,
|
||||
+ automatic=None,
|
||||
+ on_target=None,
|
||||
+ ),
|
||||
ra.types.ResourceAgentActionOcf1_0(
|
||||
name="action_2",
|
||||
timeout="12",
|
||||
@@ -158,6 +180,16 @@ class OcfVersionToOcfUnified(TestCase):
|
||||
automatic="1",
|
||||
on_target="0",
|
||||
),
|
||||
+ ra.types.ResourceAgentActionOcf1_0(
|
||||
+ name="action_4",
|
||||
+ timeout=None,
|
||||
+ interval=None,
|
||||
+ role=None,
|
||||
+ start_delay=None,
|
||||
+ depth=None,
|
||||
+ automatic="yes",
|
||||
+ on_target="True",
|
||||
+ ),
|
||||
],
|
||||
)
|
||||
metadata_out = ra.ResourceAgentMetadata(
|
||||
@@ -289,6 +321,16 @@ class OcfVersionToOcfUnified(TestCase):
|
||||
automatic=True,
|
||||
on_target=False,
|
||||
),
|
||||
+ ra.ResourceAgentAction(
|
||||
+ name="action_4",
|
||||
+ timeout=None,
|
||||
+ interval=None,
|
||||
+ role=None,
|
||||
+ start_delay=None,
|
||||
+ depth=None,
|
||||
+ automatic=True,
|
||||
+ on_target=True,
|
||||
+ ),
|
||||
],
|
||||
)
|
||||
self.assertEqual(
|
||||
diff --git a/pcs_test/tier0/lib/resource_agent/test_xml.py b/pcs_test/tier0/lib/resource_agent/test_xml.py
|
||||
index 26bbbb7d..ea055ee2 100644
|
||||
--- a/pcs_test/tier0/lib/resource_agent/test_xml.py
|
||||
+++ b/pcs_test/tier0/lib/resource_agent/test_xml.py
|
||||
@@ -164,8 +164,13 @@ class MetadataXmlToDom(TestCase):
|
||||
ra.xml._metadata_xml_to_dom("not an xml")
|
||||
|
||||
def test_no_version_not_valid(self):
|
||||
- with self.assertRaises(etree.DocumentInvalid):
|
||||
- ra.xml._metadata_xml_to_dom("<resource-agent/>")
|
||||
+ # pylint: disable=no-self-use
|
||||
+ metadata = """
|
||||
+ <resource-agent/>
|
||||
+ """
|
||||
+ assert_xml_equal(
|
||||
+ metadata, etree_to_str(ra.xml._metadata_xml_to_dom(metadata))
|
||||
+ )
|
||||
|
||||
def test_no_version_valid(self):
|
||||
# pylint: disable=no-self-use
|
||||
@@ -178,14 +183,15 @@ class MetadataXmlToDom(TestCase):
|
||||
)
|
||||
|
||||
def test_ocf_1_0_not_valid(self):
|
||||
- with self.assertRaises(etree.DocumentInvalid):
|
||||
- ra.xml._metadata_xml_to_dom(
|
||||
- """
|
||||
- <resource-agent>
|
||||
- <version>1.0</version>
|
||||
- </resource-agent>
|
||||
- """
|
||||
- )
|
||||
+ # pylint: disable=no-self-use
|
||||
+ metadata = """
|
||||
+ <resource-agent>
|
||||
+ <version>1.0</version>
|
||||
+ </resource-agent>
|
||||
+ """
|
||||
+ assert_xml_equal(
|
||||
+ metadata, etree_to_str(ra.xml._metadata_xml_to_dom(metadata))
|
||||
+ )
|
||||
|
||||
def test_ocf_1_0_valid(self):
|
||||
# pylint: disable=no-self-use
|
||||
@@ -273,19 +279,16 @@ class LoadMetadata(TestCase):
|
||||
|
||||
def test_not_valid_xml(self):
|
||||
agent_name = ra.ResourceAgentName("ocf", "pacemaker", "Dummy")
|
||||
+ metadata = "<resource-agent/>"
|
||||
self.config.runner.pcmk.load_agent(
|
||||
agent_name="ocf:pacemaker:Dummy",
|
||||
- stdout="<resource-agent/>",
|
||||
+ stdout=metadata,
|
||||
)
|
||||
|
||||
env = self.env_assist.get_env()
|
||||
- with self.assertRaises(ra.UnableToGetAgentMetadata) as cm:
|
||||
- ra.xml.load_metadata(env.cmd_runner(), agent_name)
|
||||
- self.assertEqual(cm.exception.agent_name, "ocf:pacemaker:Dummy")
|
||||
- self.assertTrue(
|
||||
- cm.exception.message.startswith(
|
||||
- "Element resource-agent failed to validate"
|
||||
- )
|
||||
+ assert_xml_equal(
|
||||
+ metadata,
|
||||
+ etree_to_str(ra.xml.load_metadata(env.cmd_runner(), agent_name)),
|
||||
)
|
||||
|
||||
|
||||
@@ -335,16 +338,15 @@ class LoadFakeAgentMetadata(TestCase):
|
||||
|
||||
def test_not_valid_xml(self):
|
||||
agent_name = ra.const.PACEMAKER_FENCED
|
||||
- self.config.runner.pcmk.load_fenced_metadata(stdout="<resource-agent/>")
|
||||
+ metadata = "<resource-agent/>"
|
||||
+ self.config.runner.pcmk.load_fenced_metadata(stdout=metadata)
|
||||
|
||||
env = self.env_assist.get_env()
|
||||
- with self.assertRaises(ra.UnableToGetAgentMetadata) as cm:
|
||||
- ra.xml.load_fake_agent_metadata(env.cmd_runner(), agent_name)
|
||||
- self.assertEqual(cm.exception.agent_name, "pacemaker-fenced")
|
||||
- self.assertTrue(
|
||||
- cm.exception.message.startswith(
|
||||
- "Element resource-agent failed to validate"
|
||||
- )
|
||||
+ assert_xml_equal(
|
||||
+ metadata,
|
||||
+ etree_to_str(
|
||||
+ ra.xml.load_fake_agent_metadata(env.cmd_runner(), agent_name)
|
||||
+ ),
|
||||
)
|
||||
|
||||
|
||||
@@ -549,19 +551,37 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
|
||||
)
|
||||
|
||||
def test_parameters_empty_parameter(self):
|
||||
- # parameters must have at least 'name' attribute
|
||||
- with self.assertRaises(ra.UnableToGetAgentMetadata):
|
||||
- self.parse(
|
||||
- self.xml(
|
||||
- """
|
||||
- <resource-agent>
|
||||
- <parameters>
|
||||
- <parameter/>
|
||||
- </parameters>
|
||||
- </resource-agent>
|
||||
- """
|
||||
- )
|
||||
- )
|
||||
+ self.assert_parse_result(
|
||||
+ self.xml(
|
||||
+ """
|
||||
+ <resource-agent>
|
||||
+ <parameters>
|
||||
+ <parameter/>
|
||||
+ </parameters>
|
||||
+ </resource-agent>
|
||||
+ """
|
||||
+ ),
|
||||
+ ResourceAgentMetadataOcf1_0(
|
||||
+ self.agent_name,
|
||||
+ shortdesc=None,
|
||||
+ longdesc=None,
|
||||
+ parameters=[
|
||||
+ ResourceAgentParameterOcf1_0(
|
||||
+ name="",
|
||||
+ shortdesc=None,
|
||||
+ longdesc=None,
|
||||
+ type="string",
|
||||
+ default=None,
|
||||
+ enum_values=None,
|
||||
+ required=None,
|
||||
+ deprecated=None,
|
||||
+ obsoletes=None,
|
||||
+ unique=None,
|
||||
+ )
|
||||
+ ],
|
||||
+ actions=[],
|
||||
+ ),
|
||||
+ )
|
||||
|
||||
def test_parameters_minimal(self):
|
||||
self.assert_parse_result(
|
||||
@@ -708,19 +728,35 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
|
||||
)
|
||||
|
||||
def test_actions_empty_action(self):
|
||||
- # actions must have at least 'name' attribute
|
||||
- with self.assertRaises(ra.UnableToGetAgentMetadata):
|
||||
- self.parse(
|
||||
- self.xml(
|
||||
- """
|
||||
- <resource-agent>
|
||||
- <actions>
|
||||
- <action/>
|
||||
- </actions>
|
||||
- </resource-agent>
|
||||
- """
|
||||
- )
|
||||
- )
|
||||
+ self.assert_parse_result(
|
||||
+ self.xml(
|
||||
+ """
|
||||
+ <resource-agent>
|
||||
+ <actions>
|
||||
+ <action/>
|
||||
+ </actions>
|
||||
+ </resource-agent>
|
||||
+ """
|
||||
+ ),
|
||||
+ ResourceAgentMetadataOcf1_0(
|
||||
+ self.agent_name,
|
||||
+ shortdesc=None,
|
||||
+ longdesc=None,
|
||||
+ parameters=[],
|
||||
+ actions=[
|
||||
+ ResourceAgentActionOcf1_0(
|
||||
+ name="",
|
||||
+ timeout=None,
|
||||
+ interval=None,
|
||||
+ role=None,
|
||||
+ start_delay=None,
|
||||
+ depth=None,
|
||||
+ automatic=None,
|
||||
+ on_target=None,
|
||||
+ ),
|
||||
+ ],
|
||||
+ ),
|
||||
+ )
|
||||
|
||||
def test_actions_multiple(self):
|
||||
self.assert_parse_result(
|
||||
@@ -787,21 +823,6 @@ class ParseOcf10NoVersion(ParseOcf10BaseMixin, TestCase):
|
||||
class ParseOcf10UnsupportedVersion(ParseOcf10BaseMixin, TestCase):
|
||||
ocf_version = "0.1.2"
|
||||
|
||||
- # These tests test that pcs raises an error if an agent doesn't conform to
|
||||
- # OCF schema. There is, however, no validation against OCF schema for
|
||||
- # agents with unsupported OCF version. That means no error message, pcs
|
||||
- # tries to process the agent and crashes. However bad that sounds, it's
|
||||
- # indended as that's how pcs behaved before OCF 1.1 was implemented.
|
||||
- # There's therefore no point in running these tests.
|
||||
-
|
||||
- def test_parameters_empty_parameter(self):
|
||||
- # parameters must have at least 'name' attribute
|
||||
- pass
|
||||
-
|
||||
- def test_actions_empty_action(self):
|
||||
- # actions must have at least 'name' attribute
|
||||
- pass
|
||||
-
|
||||
|
||||
class ParseOcf10ExplicitVersion(ParseOcf10BaseMixin, TestCase):
|
||||
ocf_version = "1.0"
|
||||
--
|
||||
2.34.1
|
||||
|
@ -1,7 +1,7 @@
|
||||
From e46d60cb36cb8ca4b153f75caa20b165945b1d26 Mon Sep 17 00:00:00 2001
|
||||
From f7230b92c946add84ed6072c20a4df5d97c77de2 Mon Sep 17 00:00:00 2001
|
||||
From: Ivan Devat <idevat@redhat.com>
|
||||
Date: Tue, 20 Nov 2018 15:03:56 +0100
|
||||
Subject: [PATCH 2/2] do not support cluster setup with udp(u) transport
|
||||
Subject: [PATCH 3/3] do not support cluster setup with udp(u) transport
|
||||
|
||||
---
|
||||
pcs/pcs.8.in | 2 ++
|
||||
@ -10,10 +10,10 @@ Subject: [PATCH 2/2] do not support cluster setup with udp(u) transport
|
||||
3 files changed, 6 insertions(+)
|
||||
|
||||
diff --git a/pcs/pcs.8.in b/pcs/pcs.8.in
|
||||
index 1695d75c..80d165fc 100644
|
||||
index 05ee66db..101d66f7 100644
|
||||
--- a/pcs/pcs.8.in
|
||||
+++ b/pcs/pcs.8.in
|
||||
@@ -429,6 +429,8 @@ By default, encryption is enabled with cipher=aes256 and hash=sha256. To disable
|
||||
@@ -436,6 +436,8 @@ By default, encryption is enabled with cipher=aes256 and hash=sha256. To disable
|
||||
|
||||
Transports udp and udpu:
|
||||
.br
|
||||
@ -23,10 +23,10 @@ index 1695d75c..80d165fc 100644
|
||||
.br
|
||||
Transport options are: ip_version, netmtu
|
||||
diff --git a/pcs/usage.py b/pcs/usage.py
|
||||
index 66e097f1..783d926d 100644
|
||||
index 78bb5ee7..b6f3dd10 100644
|
||||
--- a/pcs/usage.py
|
||||
+++ b/pcs/usage.py
|
||||
@@ -872,6 +872,7 @@ Commands:
|
||||
@@ -890,6 +890,7 @@ Commands:
|
||||
hash=sha256. To disable encryption, set cipher=none and hash=none.
|
||||
|
||||
Transports udp and udpu:
|
||||
@ -49,5 +49,5 @@ index 2f26e831..a7702ac4 100644
|
||||
#csetup-transport-options.knet .without-knet
|
||||
{
|
||||
--
|
||||
2.31.1
|
||||
2.34.1
|
||||
|
||||
|
26
SOURCES/simplify-ternar-expression.patch
Normal file
26
SOURCES/simplify-ternar-expression.patch
Normal file
@ -0,0 +1,26 @@
|
||||
From f44cdc871a39da3960bd04565b4d1d5ffa19bd23 Mon Sep 17 00:00:00 2001
|
||||
From: Ivan Devat <idevat@redhat.com>
|
||||
Date: Thu, 20 Jan 2022 13:32:49 +0100
|
||||
Subject: [PATCH 1/2] simplify ternar expression
|
||||
|
||||
The motivation for this is that covscan complains about it.
|
||||
---
|
||||
src/app/view/share/useUrlTabs.ts | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/app/view/share/useUrlTabs.ts b/src/app/view/share/useUrlTabs.ts
|
||||
index 7278dad8..a1136bf3 100644
|
||||
--- a/src/app/view/share/useUrlTabs.ts
|
||||
+++ b/src/app/view/share/useUrlTabs.ts
|
||||
@@ -13,7 +13,7 @@ export const useUrlTabs = <TABS extends ReadonlyArray<string>>(
|
||||
|
||||
return {
|
||||
currentTab,
|
||||
- matchedContext: tab !== null ? tab.matched : `/${defaultTab}`,
|
||||
+ matchedContext: tab?.matched ?? `/${defaultTab}`,
|
||||
tabList,
|
||||
};
|
||||
};
|
||||
--
|
||||
2.31.1
|
||||
|
124
SPECS/pcs.spec
124
SPECS/pcs.spec
@ -1,6 +1,6 @@
|
||||
Name: pcs
|
||||
Version: 0.10.10
|
||||
Release: 4%{?dist}.1
|
||||
Version: 0.10.12
|
||||
Release: 6%{?dist}
|
||||
# https://docs.fedoraproject.org/en-US/packaging-guidelines/LicensingGuidelines/
|
||||
# https://fedoraproject.org/wiki/Licensing:Main?rd=Licensing#Good_Licenses
|
||||
# GPLv2: pcs
|
||||
@ -20,13 +20,13 @@ Summary: Pacemaker Configuration System
|
||||
ExclusiveArch: i686 x86_64 s390x ppc64le aarch64
|
||||
|
||||
%global version_or_commit %{version}
|
||||
# %%global version_or_commit %%{version}.210-9862
|
||||
# %%global version_or_commit %%{version}.22-9d83b
|
||||
|
||||
%global pcs_source_name %{name}-%{version_or_commit}
|
||||
|
||||
# ui_commit can be determined by hash, tag or branch
|
||||
%global ui_commit 0.1.7
|
||||
%global ui_modules_version 0.1.7
|
||||
%global ui_commit 0.1.12
|
||||
%global ui_modules_version 0.1.12
|
||||
%global ui_src_name pcs-web-ui-%{ui_commit}
|
||||
|
||||
%global pcs_snmp_pkg_name pcs-snmp
|
||||
@ -115,16 +115,26 @@ Source101: https://github.com/ClusterLabs/pcs-web-ui/releases/download/%{ui_modu
|
||||
# They should come before downstream patches to avoid unnecessary conflicts.
|
||||
# Z-streams are exception here: they can come from upstream but should be
|
||||
# applied at the end to keep z-stream changes as straightforward as possible.
|
||||
|
||||
# pcs patches: <= 200
|
||||
# Patch1: bzNUMBER-01-name.patch
|
||||
Patch1: bz1998454-01-fix-creating-resources-with-depth-operation-attribut.patch
|
||||
Patch2: add-missing-file-test_stonith_update_scsi_devices.py.patch
|
||||
Patch3: bz1992668-01-add-add-remove-syntax-for-command-pcs-stonith-update.patch
|
||||
Patch4: bz1991654-01-fix-unfencing-in-pcs-stonith-update-scsi-devices.patch
|
||||
Patch1: bz2028902-01-fix-enabling-corosync-qdevice.patch
|
||||
Patch2: bz1384485-01-fix-rsc-update-cmd-when-unable-to-get-agent-metadata.patch
|
||||
Patch3: bz2032997-01-skip-checking-of-scsi-devices-to-be-removed.patch
|
||||
Patch4: bz2036633-01-Make-ocf-linbit-drbd-agent-pass-OCF-validation.patch
|
||||
Patch5: bz1990784-01-Multiple-fixes-of-pcs-resource-move-autodelete-comma.patch
|
||||
Patch6: bz2022463-01-fix-creating-empty-cib.patch
|
||||
Patch7: bz2047983-01-Fix-snmp-client.patch
|
||||
Patch8: bz2050274-01-process-invalid-OCF-agents-the-same-way-as-before.patch
|
||||
Patch9: bz2050274-02-relax-OCF-1.0-parser.patch
|
||||
|
||||
# Downstream patches do not come from upstream. They adapt pcs for specific
|
||||
# RHEL needs.
|
||||
Patch101: do-not-support-cluster-setup-with-udp-u-transport.patch
|
||||
Patch102: bz2042433-01-fix-creating-empty-cib.patch
|
||||
|
||||
# ui patches: >200
|
||||
Patch201: simplify-ternar-expression.patch
|
||||
Patch202: bz2044409-01-fix-backend-parameter-all-in-cluster-destroy.patch
|
||||
|
||||
# git for patches
|
||||
BuildRequires: git-core
|
||||
@ -266,8 +276,6 @@ Provides: bundled(pyagentx) = %{pyagentx_version}
|
||||
SNMP agent that provides information about pacemaker cluster to the master agent (snmpd)
|
||||
|
||||
%prep
|
||||
%autosetup -p1 -S git -n %{pcs_source_name}
|
||||
|
||||
# -- following is inspired by python-simplejon.el5 --
|
||||
# Update timestamps on the files touched by a patch, to avoid non-equal
|
||||
# .pyc/.pyo files across the multilib peers within a build
|
||||
@ -307,18 +315,32 @@ update_times_patch(){
|
||||
update_times ${patch_file_name} `diffstat -p1 -l ${patch_file_name}`
|
||||
}
|
||||
|
||||
# documentation for setup/autosetup/autopatch:
|
||||
# * 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 100 -a 101 -S git -n %{ui_src_name} -N
|
||||
%autopatch -p1 -m 201
|
||||
# update_times_patch %%{PATCH201}
|
||||
update_times_patch %{PATCH201}
|
||||
update_times_patch %{PATCH202}
|
||||
|
||||
# patch pcs sources
|
||||
%autosetup -S git -n %{pcs_source_name} -N
|
||||
%autopatch -p1 -M 200
|
||||
|
||||
update_times_patch %{PATCH1}
|
||||
update_times_patch %{PATCH2}
|
||||
update_times_patch %{PATCH3}
|
||||
update_times_patch %{PATCH4}
|
||||
update_times_patch %{PATCH5}
|
||||
update_times_patch %{PATCH6}
|
||||
update_times_patch %{PATCH7}
|
||||
update_times_patch %{PATCH8}
|
||||
update_times_patch %{PATCH9}
|
||||
update_times_patch %{PATCH101}
|
||||
update_times_patch %{PATCH102}
|
||||
|
||||
cp -f %SOURCE1 %{pcsd_public_dir}/images
|
||||
# prepare dirs/files necessary for building web ui
|
||||
# inside SOURCE100 is only directory %%{ui_src_name}
|
||||
tar -xzf %SOURCE100 -C %{pcsd_public_dir}
|
||||
tar -xf %SOURCE101 -C %{pcsd_public_dir}/%{ui_src_name}
|
||||
|
||||
# prepare dirs/files necessary for building all bundles
|
||||
# -----------------------------------------------------
|
||||
@ -357,19 +379,20 @@ cp -f %SOURCE45 rpm/
|
||||
%define debug_package %{nil}
|
||||
|
||||
./autogen.sh
|
||||
%{configure} --enable-local-build --enable-use-local-cache-only --enable-individual-bundling PYTHON=%{__python3}
|
||||
%{configure} --enable-local-build --enable-use-local-cache-only --enable-individual-bundling PYTHON=%{__python3} ruby_CFLAGS="%{optflags}" ruby_LIBS="%{build_ldflags}"
|
||||
make all
|
||||
|
||||
# build pcs-web-ui
|
||||
make -C %{_builddir}/%{ui_src_name} build BUILD_USE_EXISTING_NODE_MODULES=true
|
||||
|
||||
%install
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
pwd
|
||||
|
||||
%make_install
|
||||
|
||||
# build web ui and put it to pcsd
|
||||
make -C %{pcsd_public_dir}/%{ui_src_name} build
|
||||
mv %{pcsd_public_dir}/%{ui_src_name}/build ${RPM_BUILD_ROOT}%{_libdir}/%{pcsd_public_dir}/ui
|
||||
rm -r %{pcsd_public_dir}/%{ui_src_name}
|
||||
# something like make install for pcs-web-ui
|
||||
cp -r %{_builddir}/%{ui_src_name}/build ${RPM_BUILD_ROOT}%{_libdir}/%{pcsd_public_dir}/ui
|
||||
|
||||
# prepare license files
|
||||
# some rubygems do not have a license file (ruby2_keywords, thin)
|
||||
@ -429,16 +452,6 @@ rm -rf $RPM_BUILD_ROOT%{_libdir}/%{rubygem_bundle_dir}/gems/json-%{version_rubyg
|
||||
rm -rf $RPM_BUILD_ROOT%{_libdir}/%{rubygem_bundle_dir}/gems/thin-%{version_rubygem_thin}/ext
|
||||
|
||||
%check
|
||||
# In the building environment LC_CTYPE is set to C which causes tests to fail
|
||||
# due to python prints a warning about it to stderr. The following environment
|
||||
# variable disables the warning.
|
||||
# On the live system either UTF8 locale is set or the warning is emmited
|
||||
# which breaks pcs. That is the correct behavior since with wrong locales it
|
||||
# would be probably broken anyway.
|
||||
# The main concern here is to make the tests pass.
|
||||
# See https://fedoraproject.org/wiki/Changes/python3_c.utf-8_locale for details.
|
||||
export PYTHONCOERCECLOCALE=0
|
||||
|
||||
run_all_tests(){
|
||||
#run pcs tests
|
||||
|
||||
@ -450,7 +463,6 @@ run_all_tests(){
|
||||
# TODO: Investigate the issue
|
||||
|
||||
%{__python3} pcs_test/suite --tier0 -v --vanilla --all-but \
|
||||
pcs_test.tier0.lib.commands.test_resource_agent.DescribeAgentUtf8.test_describe \
|
||||
pcs_test.tier0.daemon.app.test_app_remote.SyncConfigMutualExclusive.test_get_not_locked \
|
||||
pcs_test.tier0.daemon.app.test_app_remote.SyncConfigMutualExclusive.test_post_not_locked \
|
||||
|
||||
@ -575,17 +587,47 @@ remove_all_tests
|
||||
%license pyagentx_LICENSE.txt
|
||||
|
||||
%changelog
|
||||
* Wed Jan 26 2022 Miroslav Lisik <mlisik@redhat.com> - 0.10.10-4.el8_5.1
|
||||
* Fri Feb 11 2022 Miroslav Lisik <mlisik@redhat.com> - 0.10.12-6
|
||||
- Fixed processing agents not conforming to OCF schema
|
||||
- Resolves: rhbz#2050274
|
||||
|
||||
* Tue Feb 01 2022 Miroslav Lisik <mlisik@redhat.com> - 0.10.12-5
|
||||
- Fixed snmp client
|
||||
- Resolves: rhbz#2047983
|
||||
|
||||
* Tue Jan 25 2022 Miroslav Lisik <mlisik@redhat.com> - 0.10.12-4
|
||||
- Fixed cluster destroy in web ui
|
||||
- Fixed covscan issue in web ui
|
||||
- Resolves: rhbz#1970508
|
||||
|
||||
* Fri Jan 14 2022 Miroslav Lisik <mlisik@redhat.com> - 0.10.12-3
|
||||
- Fixed 'pcs resource move --autodelete' command
|
||||
- Fixed removing of unavailable fence-scsi storage device
|
||||
- Fixed ocf validation of ocf linbit drdb agent
|
||||
- Fixed creating empty cib
|
||||
- Resolves: rhbz#bz2042433
|
||||
- Updated pcs-web-ui
|
||||
- Resolves: rhbz#1990784 rhbz#2022463 rhbz#2032997 rhbz#2036633
|
||||
|
||||
* Fri Sep 24 2021 Miroslav Lisik <mlisik@redhat.com> - 0.10.10-4
|
||||
- Fixed unfencing in `pcs stonith update-scsi-devices`
|
||||
- Resolves: rhbz#bz1991654
|
||||
* Wed Dec 15 2021 Miroslav Lisik <mlisik@redhat.com> - 0.10.12-2
|
||||
- Fixed rsc update cmd when unable to get agent metadata
|
||||
- Fixed enabling corosync-qdevice
|
||||
- Resolves: rhbz#1384485 rhbz#2028902
|
||||
|
||||
* Fri Sep 10 2021 Miroslav Lisik <mlisik@redhat.com> - 0.10.10-3
|
||||
- Added add/remove syntax for command `pcs stonith update-scsi-devices`
|
||||
- Resolves: rhbz#1992668
|
||||
* Thu Dec 02 2021 Miroslav Lisik <mlisik@redhat.com> - 0.10.12-1
|
||||
- Rebased to latest upstream sources (see CHANGELOG.md)
|
||||
- Updated pcs-web-ui
|
||||
- Resolves: rhbz#1552470 rhbz#1997011 rhbz#2017311 rhbz#2017312 rhbz#2024543 rhbz#2012128
|
||||
|
||||
* Mon Nov 22 2021 Miroslav Lisik <mlisik@redhat.com> - 0.10.11-2
|
||||
- Rebased to latest upstream sources (see CHANGELOG.md)
|
||||
- Removed 'export PYTHONCOERCECLOCALE=0'
|
||||
- Resolves: rhbz#1384485 rhbz#1936833 rhbz#1968088 rhbz#1990784 rhbz#2012128
|
||||
|
||||
* Mon Nov 01 2021 Miroslav Lisik <mlisik@redhat.com> - 0.10.11-1
|
||||
- Rebased to latest upstream sources (see CHANGELOG.md)
|
||||
- Updated pcs-web-ui
|
||||
- Enabled wui patching
|
||||
- Resolves: rhbz#1533090 rhbz#1970508 rhbz#1997011 rhbz#2003066 rhbz#2003068 rhbz#2012128
|
||||
|
||||
* Fri Aug 27 2021 Miroslav Lisik <mlisik@redhat.com> - 0.10.10-2
|
||||
- Fixed create resources with depth operation attribute
|
||||
|
Loading…
Reference in New Issue
Block a user