Auto sync2gitlab import of pcs-0.10.12-6.el8.src.rpm

This commit is contained in:
James Antill 2022-05-26 12:47:46 -04:00
parent e018c48124
commit 838a12113f
16 changed files with 4139 additions and 1 deletions

25
.gitignore vendored Normal file
View File

@ -0,0 +1,25 @@
/HAM-logo.png
/backports-3.17.2.gem
/dacite-1.6.0.tar.gz
/daemons-1.3.1.gem
/dataclasses-0.8.tar.gz
/ethon-0.12.0.gem
/eventmachine-1.2.7.gem
/ffi-1.13.1.gem
/json-2.3.0.gem
/mustermann-1.1.1.gem
/open4-1.3.4-1.gem
/pcs-0.10.12.tar.gz
/pcs-web-ui-0.1.12.tar.gz
/pcs-web-ui-node-modules-0.1.12.tar.xz
/pyagentx-0.4.pcs.2.tar.gz
/python-dateutil-2.8.1.tar.gz
/rack-2.2.3.gem
/rack-protection-2.0.8.1.gem
/rack-test-1.1.0.gem
/rexml-3.2.5.gem
/ruby2_keywords-0.0.2.gem
/sinatra-2.0.8.1.gem
/thin-1.7.2.gem
/tilt-2.0.10.gem
/tornado-6.1.0.tar.gz

1
EMPTY
View File

@ -1 +0,0 @@

View File

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

View File

@ -0,0 +1,94 @@
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 4/5] fix creating empty cib
---
pcs/utils.py | 21 +++++++++++----------
pcs_test/tier1/test_misc.py | 25 ++++++++++++++++++++++++-
2 files changed, 35 insertions(+), 11 deletions(-)
diff --git a/pcs/utils.py b/pcs/utils.py
index ad2d4452..423ffc43 100644
--- a/pcs/utils.py
+++ b/pcs/utils.py
@@ -2067,16 +2067,17 @@ def write_empty_cib(cibfile):
"""
Commandline options: no options
"""
- empty_xml = """<?xml version="1.0" encoding="UTF-8"?>
-<cib admin_epoch="0" epoch="1" num_updates="1" validate-with="pacemaker-1.2">
- <configuration>
- <crm_config/>
- <nodes/>
- <resources/>
- <constraints/>
- </configuration>
- <status/>
-</cib>"""
+ empty_xml = """
+ <cib admin_epoch="0" epoch="1" num_updates="1" validate-with="pacemaker-3.1">
+ <configuration>
+ <crm_config/>
+ <nodes/>
+ <resources/>
+ <constraints/>
+ </configuration>
+ <status/>
+ </cib>
+ """
with open(cibfile, "w") as f:
f.write(empty_xml)
diff --git a/pcs_test/tier1/test_misc.py b/pcs_test/tier1/test_misc.py
index 29ca6a71..6e6f72fb 100644
--- a/pcs_test/tier1/test_misc.py
+++ b/pcs_test/tier1/test_misc.py
@@ -1,8 +1,10 @@
+import os
from unittest import TestCase
from pcs_test.tools.assertions import AssertPcsMixin
from pcs_test.tools.misc import (
get_test_resource as rc,
+ get_tmp_dir,
get_tmp_file,
outdent,
write_file_to_tmpfile,
@@ -19,7 +21,7 @@ class ParseArgvDashDash(TestCase, AssertPcsMixin):
cmd = "constraint colocation add R1 with R2".split()
def setUp(self):
- self.temp_cib = get_tmp_file("tier1_misc")
+ self.temp_cib = get_tmp_file("tier1_misc_dashdash")
write_file_to_tmpfile(rc("cib-empty.xml"), self.temp_cib)
self.pcs_runner = PcsRunner(self.temp_cib.name)
self.allowed_roles = format_list(const.PCMK_ROLES)
@@ -89,3 +91,24 @@ class ParseArgvDashDash(TestCase, AssertPcsMixin):
"""
),
)
+
+
+class EmptyCibIsPcmk2Compatible(TestCase, AssertPcsMixin):
+ # This test verifies that a default empty CIB created by pcs when -f points
+ # to an empty file conforms to minimal schema version supported by
+ # pacemaker 2.0. If pcs prints a message that CIB schema has been upgraded,
+ # then the test fails and shows there is a bug. Bundle with promoted-max
+ # requires CIB compliant with schema 3.1, which was introduced in pacemaker
+ # 2.0.0.
+ def setUp(self):
+ self.cib_dir = get_tmp_dir("tier1_misc_empty_cib")
+ self.pcs_runner = PcsRunner(os.path.join(self.cib_dir.name, "cib.xml"))
+
+ def tearDown(self):
+ self.cib_dir.cleanup()
+
+ def test_success(self):
+ self.assert_pcs_success(
+ "resource bundle create b container docker image=my.img promoted-max=1".split(),
+ "",
+ )
--
2.31.1

View 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

View File

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

View File

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

View File

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

View 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

View File

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

View 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

View File

@ -0,0 +1,53 @@
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 3/3] do not support cluster setup with udp(u) transport
---
pcs/pcs.8.in | 2 ++
pcs/usage.py | 1 +
pcsd/public/css/style.css | 3 +++
3 files changed, 6 insertions(+)
diff --git a/pcs/pcs.8.in b/pcs/pcs.8.in
index 05ee66db..101d66f7 100644
--- a/pcs/pcs.8.in
+++ b/pcs/pcs.8.in
@@ -436,6 +436,8 @@ By default, encryption is enabled with cipher=aes256 and hash=sha256. To disable
Transports udp and udpu:
.br
+WARNING: These transports are not supported in RHEL 8.
+.br
These transports are limited to one address per node. They do not support traffic encryption nor compression.
.br
Transport options are: ip_version, netmtu
diff --git a/pcs/usage.py b/pcs/usage.py
index 78bb5ee7..b6f3dd10 100644
--- a/pcs/usage.py
+++ b/pcs/usage.py
@@ -890,6 +890,7 @@ Commands:
hash=sha256. To disable encryption, set cipher=none and hash=none.
Transports udp and udpu:
+ WARNING: These transports are not supported in RHEL 8.
These transports are limited to one address per node. They do not
support traffic encryption nor compression.
Transport options are:
diff --git a/pcsd/public/css/style.css b/pcsd/public/css/style.css
index 2f26e831..a7702ac4 100644
--- a/pcsd/public/css/style.css
+++ b/pcsd/public/css/style.css
@@ -949,6 +949,9 @@ table.args-table td.reg {
width: 6ch;
text-align: right;
}
+#csetup-transport .transport-types {
+ display: none;
+}
#csetup-transport-options.udp .knet-only,
#csetup-transport-options.knet .without-knet
{
--
2.34.1

1091
pcs.spec Normal file

File diff suppressed because it is too large Load Diff

View 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

25
sources Normal file
View File

@ -0,0 +1,25 @@
SHA512 (HAM-logo.png) = bf4f865c5c9c3b3f91354411f68e4295bb80e69e4cbcb720652bde9ad726dc0aa2610b5ab3c8f17857caac30192f9a226009436cf6ffc0f62651146512bf92d2
SHA512 (backports-3.17.2.gem) = e860e4c1784b49e81294ce0bdd27226e28bbec9163398d79206c8dced3324c7625bf75c5ccf09bbcee4cf7bfa96371f37572e6d2d8a22dd2204a441eccd716fb
SHA512 (dacite-1.6.0.tar.gz) = 034255f095589d309fe5805413d8b148f430cd20a0de305b7954083b530d516da1d8f3f00ebb5264a8cfb77f2b2a76f1e2d863e78bd191f1d85021c5553815da
SHA512 (daemons-1.3.1.gem) = eede065019b5e251e5b7d0959251c8591ec8c38ada6861a1c41cf85959666a4865efc69178f63bf2acfc1e993c8222d581ac5d689be439744ee3cef0ca6f5138
SHA512 (dataclasses-0.8.tar.gz) = 3369f5a9b1243f0af9e05dcfa8534801137ab05c62416360e6d36eee2df23b828f4a0d777aad706b7eb16ee71b879390abf4e402def2930f4fc4ccb41a6bbd88
SHA512 (ethon-0.12.0.gem) = 5fea666efdd5e2d568d0e074f205525656e202ecea3bb5ff8a8ab838a596cab349b869baffb7926227d6cfdae5b9c1cb86bb201b4c9585056c6671ab4feee596
SHA512 (eventmachine-1.2.7.gem) = fdbcf9fc933e2414e70f8f48153e9ba6ed7a0029cdf49cdcb4ab72ab26683e727a36c099f017f20681f9c361179461743e501278ca9bd5612e693e26867cc516
SHA512 (ffi-1.13.1.gem) = 4c0b5086463c5abedc440c44fb12c62612627a2aaad60b1959ea8831d4ec2bbe3217fec0b63612730bb219748fd2bd2252dd615ca0305fb7f0e8456c63c31fbd
SHA512 (json-2.3.0.gem) = 9086a64b664f2d774439c0c9f10c2c1cd897684050eb60f8e3ad9256e0b9541deec3bd22fab0bdcf66e3d5bc795720c6923d514b6edb824c9962fc22f162b332
SHA512 (mustermann-1.1.1.gem) = 55d5fd9b8309e0b806b2fb0af409fbc71b958b8f91116991439f2c66b1f71f17647a61020b0349a86132e7d3da383121719ea330538abb8f3c8bef5fbff0b747
SHA512 (open4-1.3.4-1.gem) = 838a18efcd093d55d9589ff9d5c11054618abef863224c2d9b31445dc735218c2f96d954040e2d3f8d5aab0140e54b627fcc4a1b01c17e59267402a2abdd8efb
SHA512 (pcs-0.10.12.tar.gz) = 184ea59fa8ccb80f98f134f36f41443d9359832ad873ee1931b1ffa5c3ff200466e99a57f8bc38a2c2562c3040d295db497bd31f2bdd3bb82875f0ff141a5339
SHA512 (pcs-web-ui-0.1.12.tar.gz) = 0c1e3e71ce4cf5a1a39319b379985267642ed36d54bc18c6b0c4c5c9f8cd24e59c4f6137a64ffe704fe22a056960c3a205308dba17060680f3bbd92abe9ab9f4
SHA512 (pcs-web-ui-node-modules-0.1.12.tar.xz) = 4f72f5f239613ab93ce1f32126050fae1b1c00ec3b73799a1ace3687c1a35370168ff1b6e070c2b661ece84e3ed4463189453247a2799f20dac1028b0c9b83f6
SHA512 (pyagentx-0.4.pcs.2.tar.gz) = d4194fec9a3e5fefe3793d49b7fec1feafef294c7e613a06046c2993daeefc5cb39d7c5b2b402ff83e49b2d976953f862264288c758c0be09d997b5323cc558a
SHA512 (python-dateutil-2.8.1.tar.gz) = 337000216e0f8ce32d6363768444144183ab9268f69082f20858f2b3322b1c449e53b2f2b5dcb3645be22294659ce7838f74ace2fd7a7c4f2adc6cf806a9fa2c
SHA512 (rack-2.2.3.gem) = aabda2ac4aeea6b119c5d570a6c36b5c114f879cc73678a6f385b71f2191501a86adc3bed6f0e0bacfc1e4c48c2374714588669ede898053dc7719899bf71635
SHA512 (rack-protection-2.0.8.1.gem) = 264aef904d9ae1c38197ad4ad44f1638905bd180fc4bb402c47856c37168059e8e975ca2de5518b1e5cebf79b970024b7d9f5f8b8908880451357dea5e428562
SHA512 (rack-test-1.1.0.gem) = 16e291fa5a88b6866e8057b4bf1aae4ffe17dd9b0300c1ee36632c5b21ff2075cb4356f6f78437dd84ea76047a5d3abe3dda087a2c154f5e5712e62d7e57fdc9
SHA512 (rexml-3.2.5.gem) = 1e3838d4a5befa76137fb8fea6a20195490645aa2b1c5d14d1eeca6c093d7f64eb405f07fd07b00fcafa9606dc78f9f0a488012338f81414623feb6e8cb83931
SHA512 (ruby2_keywords-0.0.2.gem) = a7bc02980e073a899bf3de2e32711ca9820657d43cc44dede44ca31681d8586e7f808a93b6f6778aaaaf8b84da37a48c8062e69ca33477d9469e9634d616ed8d
SHA512 (sinatra-2.0.8.1.gem) = a63c8bbf468d059ce1e545c4da72208db34ef677141540b31a24a82537c15bbc020c712b9d9eb49470af45b4ce27f65ab21191ee47ebae7c45b9bbd86600ea3e
SHA512 (thin-1.7.2.gem) = e9e0ad3dab77a1c6f3e413ce7ed1598da0db5fa62355a1fbbc73153d2fd810d82d5bf2e6a434429912eb885c263c674364a5dec7d878960e2dbef37ccbe1472b
SHA512 (tilt-2.0.10.gem) = d2e0e1976da24ea4d8581d29a3ac2c0772c2e42f1cd04c48f1e3c1745a14d7cd319f14cead3e5341ec0f6c07aa216b50c29ad96984a85a9757ff9f7cc89b80df
SHA512 (tornado-6.1.0.tar.gz) = bd161a1c30f40f983d608297bca113735cb4baad255de71302a5b4d35be8c02afbc9820728efa912e62e1cbbfad8f92360261a69e0c8759f9e6cb477fbca31c7