Resolves: rhbz#2179901 rhbz#2180697 rhbz#2180704 rhbz#2180708 rhbz#2180978 rhbz#2183180
- Fix displaying differences between configuration checkpoints in “pcs config checkpoint diff” command - Fix “pcs stonith update-scsi-devices” command which was broken since Pacemaker-2.1.5-rc1 - Fixed loading of cluster status in the web interface when fencing levels are configured - Fixed a vulnerability in pcs-web-ui-node-modules - Updated bundled rubygem rack
This commit is contained in:
parent
f93843e2c6
commit
5fdda3ad9a
3
.gitignore
vendored
3
.gitignore
vendored
@ -188,3 +188,6 @@
|
|||||||
/rack-2.2.5.gem
|
/rack-2.2.5.gem
|
||||||
/rack-protection-3.0.5.gem
|
/rack-protection-3.0.5.gem
|
||||||
/sinatra-3.0.5.gem
|
/sinatra-3.0.5.gem
|
||||||
|
/rack-2.2.6.4.gem
|
||||||
|
/pcs-web-ui-0.1.16.1.tar.gz
|
||||||
|
/pcs-web-ui-node-modules-0.1.16.1.tar.xz
|
||||||
|
121
bz2180697-01-fix-pcs-config-checkpoint-diff.patch
Normal file
121
bz2180697-01-fix-pcs-config-checkpoint-diff.patch
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
From d1a658601487175ec70054e56ade116f3dbcecf6 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Miroslav Lisik <mlisik@redhat.com>
|
||||||
|
Date: Mon, 6 Mar 2023 15:42:35 +0100
|
||||||
|
Subject: [PATCH 1/2] fix `pcs config checkpoint diff` command
|
||||||
|
|
||||||
|
---
|
||||||
|
CHANGELOG.md | 26 --------------------------
|
||||||
|
pcs/cli/common/lib_wrapper.py | 15 +--------------
|
||||||
|
pcs/config.py | 3 +++
|
||||||
|
3 files changed, 4 insertions(+), 40 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/CHANGELOG.md b/CHANGELOG.md
|
||||||
|
index 0945d727..7d3d606b 100644
|
||||||
|
--- a/CHANGELOG.md
|
||||||
|
+++ b/CHANGELOG.md
|
||||||
|
@@ -1,31 +1,5 @@
|
||||||
|
# Change Log
|
||||||
|
|
||||||
|
-## [Unreleased]
|
||||||
|
-
|
||||||
|
-### Added
|
||||||
|
-- Warning to `pcs resource|stonith update` commands about not using agent
|
||||||
|
- self-validation feature when the resource is already misconfigured
|
||||||
|
- ([rhbz#2151524])
|
||||||
|
-
|
||||||
|
-### Fixed
|
||||||
|
-- Graceful stopping pcsd service using `systemctl stop pcsd` command
|
||||||
|
-- Displaying bool and integer values in `pcs resource config` command
|
||||||
|
- ([rhbz#2151164], [ghissue#604])
|
||||||
|
-- Allow time values in stonith-watchdog-time property ([rhbz#2158790])
|
||||||
|
-
|
||||||
|
-### Changed
|
||||||
|
-- Resource/stonith agent self-validation of instance attributes is now
|
||||||
|
- disabled by default, as many agents do not work with it properly.
|
||||||
|
- Use flag '--agent-validation' to enable it in supported commands.
|
||||||
|
- ([rhbz#2159454])
|
||||||
|
-
|
||||||
|
-[ghissue#604]: https://github.com/ClusterLabs/pcs/issues/604
|
||||||
|
-[rhbz#2151164]: https://bugzilla.redhat.com/show_bug.cgi?id=2151164
|
||||||
|
-[rhbz#2151524]: https://bugzilla.redhat.com/show_bug.cgi?id=2151524
|
||||||
|
-[rhbz#2159454]: https://bugzilla.redhat.com/show_bug.cgi?id=2159454
|
||||||
|
-[rhbz#2158790]: https://bugzilla.redhat.com/show_bug.cgi?id=2158790
|
||||||
|
-
|
||||||
|
-
|
||||||
|
## [0.11.4] - 2022-11-21
|
||||||
|
|
||||||
|
### Security
|
||||||
|
diff --git a/pcs/cli/common/lib_wrapper.py b/pcs/cli/common/lib_wrapper.py
|
||||||
|
index 217bfe3e..e6411e3c 100644
|
||||||
|
--- a/pcs/cli/common/lib_wrapper.py
|
||||||
|
+++ b/pcs/cli/common/lib_wrapper.py
|
||||||
|
@@ -1,9 +1,5 @@
|
||||||
|
import logging
|
||||||
|
from collections import namedtuple
|
||||||
|
-from typing import (
|
||||||
|
- Any,
|
||||||
|
- Dict,
|
||||||
|
-)
|
||||||
|
|
||||||
|
from pcs import settings
|
||||||
|
from pcs.cli.common import middleware
|
||||||
|
@@ -36,9 +32,6 @@ from pcs.lib.commands.constraint import order as constraint_order
|
||||||
|
from pcs.lib.commands.constraint import ticket as constraint_ticket
|
||||||
|
from pcs.lib.env import LibraryEnvironment
|
||||||
|
|
||||||
|
-# Note: not properly typed
|
||||||
|
-_CACHE: Dict[Any, Any] = {}
|
||||||
|
-
|
||||||
|
|
||||||
|
def wrapper(dictionary):
|
||||||
|
return namedtuple("wrapper", dictionary.keys())(**dictionary)
|
||||||
|
@@ -106,12 +99,6 @@ def bind_all(env, run_with_middleware, dictionary):
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
-def get_module(env, middleware_factory, name):
|
||||||
|
- if name not in _CACHE:
|
||||||
|
- _CACHE[name] = load_module(env, middleware_factory, name)
|
||||||
|
- return _CACHE[name]
|
||||||
|
-
|
||||||
|
-
|
||||||
|
def load_module(env, middleware_factory, name):
|
||||||
|
# pylint: disable=too-many-return-statements, too-many-branches
|
||||||
|
if name == "acl":
|
||||||
|
@@ -544,4 +531,4 @@ class Library:
|
||||||
|
self.middleware_factory = middleware_factory
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
- return get_module(self.env, self.middleware_factory, name)
|
||||||
|
+ return load_module(self.env, self.middleware_factory, name)
|
||||||
|
diff --git a/pcs/config.py b/pcs/config.py
|
||||||
|
index e0d179f0..6da1151b 100644
|
||||||
|
--- a/pcs/config.py
|
||||||
|
+++ b/pcs/config.py
|
||||||
|
@@ -691,6 +691,7 @@ def _checkpoint_to_lines(lib, checkpoint_number):
|
||||||
|
orig_usefile = utils.usefile
|
||||||
|
orig_filename = utils.filename
|
||||||
|
orig_middleware = lib.middleware_factory
|
||||||
|
+ orig_env = lib.env
|
||||||
|
# configure old code to read the CIB from a file
|
||||||
|
utils.usefile = True
|
||||||
|
utils.filename = os.path.join(
|
||||||
|
@@ -700,6 +701,7 @@ def _checkpoint_to_lines(lib, checkpoint_number):
|
||||||
|
lib.middleware_factory = orig_middleware._replace(
|
||||||
|
cib=middleware.cib(utils.filename, utils.touch_cib_file)
|
||||||
|
)
|
||||||
|
+ lib.env = utils.get_cli_env()
|
||||||
|
# export the CIB to text
|
||||||
|
result = False, []
|
||||||
|
if os.path.isfile(utils.filename):
|
||||||
|
@@ -708,6 +710,7 @@ def _checkpoint_to_lines(lib, checkpoint_number):
|
||||||
|
utils.usefile = orig_usefile
|
||||||
|
utils.filename = orig_filename
|
||||||
|
lib.middleware_factory = orig_middleware
|
||||||
|
+ lib.env = orig_env
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
2.39.2
|
||||||
|
|
975
bz2180704-01-fix-pcs-stonith-update-scsi.patch
Normal file
975
bz2180704-01-fix-pcs-stonith-update-scsi.patch
Normal file
@ -0,0 +1,975 @@
|
|||||||
|
From 6841064bf1d06e16c9c5bf9a6ab42b3185d55afb Mon Sep 17 00:00:00 2001
|
||||||
|
From: Miroslav Lisik <mlisik@redhat.com>
|
||||||
|
Date: Mon, 20 Mar 2023 10:35:34 +0100
|
||||||
|
Subject: [PATCH 2/2] fix `pcs stonith update-scsi-devices` command
|
||||||
|
|
||||||
|
---
|
||||||
|
pcs/lib/cib/resource/stonith.py | 168 +++++-
|
||||||
|
.../test_stonith_update_scsi_devices.py | 571 ++++++++++++++----
|
||||||
|
2 files changed, 601 insertions(+), 138 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/pcs/lib/cib/resource/stonith.py b/pcs/lib/cib/resource/stonith.py
|
||||||
|
index 1f5bddff..07cffba6 100644
|
||||||
|
--- a/pcs/lib/cib/resource/stonith.py
|
||||||
|
+++ b/pcs/lib/cib/resource/stonith.py
|
||||||
|
@@ -169,12 +169,64 @@ def get_node_key_map_for_mpath(
|
||||||
|
return node_key_map
|
||||||
|
|
||||||
|
|
||||||
|
-DIGEST_ATTRS = ["op-digest", "op-secure-digest", "op-restart-digest"]
|
||||||
|
-DIGEST_ATTR_TO_TYPE_MAP = {
|
||||||
|
+DIGEST_ATTR_TO_DIGEST_TYPE_MAP = {
|
||||||
|
"op-digest": "all",
|
||||||
|
"op-secure-digest": "nonprivate",
|
||||||
|
"op-restart-digest": "nonreloadable",
|
||||||
|
}
|
||||||
|
+TRANSIENT_DIGEST_ATTR_TO_DIGEST_TYPE_MAP = {
|
||||||
|
+ "#digests-all": "all",
|
||||||
|
+ "#digests-secure": "nonprivate",
|
||||||
|
+}
|
||||||
|
+DIGEST_ATTRS = frozenset(DIGEST_ATTR_TO_DIGEST_TYPE_MAP.keys())
|
||||||
|
+TRANSIENT_DIGEST_ATTRS = frozenset(
|
||||||
|
+ TRANSIENT_DIGEST_ATTR_TO_DIGEST_TYPE_MAP.keys()
|
||||||
|
+)
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def _get_digest(
|
||||||
|
+ attr: str,
|
||||||
|
+ attr_to_type_map: Dict[str, str],
|
||||||
|
+ calculated_digests: Dict[str, Optional[str]],
|
||||||
|
+) -> str:
|
||||||
|
+ """
|
||||||
|
+ Return digest of right type for the specified attribute. If missing, raise
|
||||||
|
+ an error.
|
||||||
|
+
|
||||||
|
+ attr -- name of digest attribute
|
||||||
|
+ atttr_to_type_map -- map for attribute name to digest type conversion
|
||||||
|
+ calculated_digests -- digests calculated by pacemaker
|
||||||
|
+ """
|
||||||
|
+ if attr not in attr_to_type_map:
|
||||||
|
+ raise AssertionError(
|
||||||
|
+ f"Key '{attr}' is missing in the attribute name to digest type map"
|
||||||
|
+ )
|
||||||
|
+ digest = calculated_digests.get(attr_to_type_map[attr])
|
||||||
|
+ if digest is None:
|
||||||
|
+ # this should not happen and when it does it is pacemaker fault
|
||||||
|
+ raise LibraryError(
|
||||||
|
+ ReportItem.error(
|
||||||
|
+ reports.messages.StonithRestartlessUpdateUnableToPerform(
|
||||||
|
+ f"necessary digest for '{attr}' attribute is missing"
|
||||||
|
+ )
|
||||||
|
+ )
|
||||||
|
+ )
|
||||||
|
+ return digest
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def _get_transient_instance_attributes(cib: _Element) -> List[_Element]:
|
||||||
|
+ """
|
||||||
|
+ Return list of instance_attributes elements which could contain digest
|
||||||
|
+ attributes.
|
||||||
|
+
|
||||||
|
+ cib -- CIB root element
|
||||||
|
+ """
|
||||||
|
+ return cast(
|
||||||
|
+ List[_Element],
|
||||||
|
+ cib.xpath(
|
||||||
|
+ "./status/node_state/transient_attributes/instance_attributes"
|
||||||
|
+ ),
|
||||||
|
+ )
|
||||||
|
|
||||||
|
|
||||||
|
def _get_lrm_rsc_op_elements(
|
||||||
|
@@ -278,21 +330,89 @@ def _update_digest_attrs_in_lrm_rsc_op(
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for attr in common_digests_attrs:
|
||||||
|
- new_digest = calculated_digests[DIGEST_ATTR_TO_TYPE_MAP[attr]]
|
||||||
|
- if new_digest is None:
|
||||||
|
- # this should not happen and when it does it is pacemaker fault
|
||||||
|
+ # update digest in cib
|
||||||
|
+ lrm_rsc_op.attrib[attr] = _get_digest(
|
||||||
|
+ attr, DIGEST_ATTR_TO_DIGEST_TYPE_MAP, calculated_digests
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def _get_transient_digest_value(
|
||||||
|
+ old_value: str, stonith_id: str, stonith_type: str, digest: str
|
||||||
|
+) -> str:
|
||||||
|
+ """
|
||||||
|
+ Return transient digest value with replaced digest.
|
||||||
|
+
|
||||||
|
+ Value has comma separated format:
|
||||||
|
+ <stonith_id>:<stonith_type>:<digest>,...
|
||||||
|
+
|
||||||
|
+ and we need to replace only digest for our currently updated stonith device.
|
||||||
|
+
|
||||||
|
+ old_value -- value to be replaced
|
||||||
|
+ stonith_id -- id of stonith resource
|
||||||
|
+ stonith_type -- stonith resource type
|
||||||
|
+ digest -- digest for new value
|
||||||
|
+ """
|
||||||
|
+ new_comma_values_list = []
|
||||||
|
+ for comma_value in old_value.split(","):
|
||||||
|
+ if comma_value:
|
||||||
|
+ try:
|
||||||
|
+ _id, _type, _ = comma_value.split(":")
|
||||||
|
+ except ValueError as e:
|
||||||
|
+ raise LibraryError(
|
||||||
|
+ ReportItem.error(
|
||||||
|
+ reports.messages.StonithRestartlessUpdateUnableToPerform(
|
||||||
|
+ f"invalid digest attribute value: '{old_value}'"
|
||||||
|
+ )
|
||||||
|
+ )
|
||||||
|
+ ) from e
|
||||||
|
+ if _id == stonith_id and _type == stonith_type:
|
||||||
|
+ comma_value = ":".join([stonith_id, stonith_type, digest])
|
||||||
|
+ new_comma_values_list.append(comma_value)
|
||||||
|
+ return ",".join(new_comma_values_list)
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def _update_digest_attrs_in_transient_instance_attributes(
|
||||||
|
+ nvset_el: _Element,
|
||||||
|
+ stonith_id: str,
|
||||||
|
+ stonith_type: str,
|
||||||
|
+ calculated_digests: Dict[str, Optional[str]],
|
||||||
|
+) -> None:
|
||||||
|
+ """
|
||||||
|
+ Update digests attributes in transient instance attributes element.
|
||||||
|
+
|
||||||
|
+ nvset_el -- instance_attributes element containing nvpairs with digests
|
||||||
|
+ attributes
|
||||||
|
+ stonith_id -- id of stonith resource being updated
|
||||||
|
+ stonith_type -- type of stonith resource being updated
|
||||||
|
+ calculated_digests -- digests calculated by pacemaker
|
||||||
|
+ """
|
||||||
|
+ for attr in TRANSIENT_DIGEST_ATTRS:
|
||||||
|
+ nvpair_list = cast(
|
||||||
|
+ List[_Element],
|
||||||
|
+ nvset_el.xpath("./nvpair[@name=$name]", name=attr),
|
||||||
|
+ )
|
||||||
|
+ if not nvpair_list:
|
||||||
|
+ continue
|
||||||
|
+ if len(nvpair_list) > 1:
|
||||||
|
raise LibraryError(
|
||||||
|
ReportItem.error(
|
||||||
|
reports.messages.StonithRestartlessUpdateUnableToPerform(
|
||||||
|
- (
|
||||||
|
- f"necessary digest for '{attr}' attribute is "
|
||||||
|
- "missing"
|
||||||
|
- )
|
||||||
|
+ f"multiple digests attributes: '{attr}'"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
- # update digest in cib
|
||||||
|
- lrm_rsc_op.attrib[attr] = new_digest
|
||||||
|
+ old_value = nvpair_list[0].attrib["value"]
|
||||||
|
+ if old_value:
|
||||||
|
+ nvpair_list[0].attrib["value"] = _get_transient_digest_value(
|
||||||
|
+ str(old_value),
|
||||||
|
+ stonith_id,
|
||||||
|
+ stonith_type,
|
||||||
|
+ _get_digest(
|
||||||
|
+ attr,
|
||||||
|
+ TRANSIENT_DIGEST_ATTR_TO_DIGEST_TYPE_MAP,
|
||||||
|
+ calculated_digests,
|
||||||
|
+ ),
|
||||||
|
+ )
|
||||||
|
|
||||||
|
|
||||||
|
def update_scsi_devices_without_restart(
|
||||||
|
@@ -311,6 +431,8 @@ def update_scsi_devices_without_restart(
|
||||||
|
id_provider -- elements' ids generator
|
||||||
|
device_list -- list of updated scsi devices
|
||||||
|
"""
|
||||||
|
+ # pylint: disable=too-many-locals
|
||||||
|
+ cib = get_root(resource_el)
|
||||||
|
resource_id = resource_el.get("id", "")
|
||||||
|
roles_with_nodes = get_resource_state(cluster_state, resource_id)
|
||||||
|
if "Started" not in roles_with_nodes:
|
||||||
|
@@ -341,17 +463,14 @@ def update_scsi_devices_without_restart(
|
||||||
|
)
|
||||||
|
|
||||||
|
lrm_rsc_op_start_list = _get_lrm_rsc_op_elements(
|
||||||
|
- get_root(resource_el), resource_id, node_name, "start"
|
||||||
|
+ cib, resource_id, node_name, "start"
|
||||||
|
+ )
|
||||||
|
+ new_instance_attrs_digests = get_resource_digests(
|
||||||
|
+ runner, resource_id, node_name, new_instance_attrs
|
||||||
|
)
|
||||||
|
if len(lrm_rsc_op_start_list) == 1:
|
||||||
|
_update_digest_attrs_in_lrm_rsc_op(
|
||||||
|
- lrm_rsc_op_start_list[0],
|
||||||
|
- get_resource_digests(
|
||||||
|
- runner,
|
||||||
|
- resource_id,
|
||||||
|
- node_name,
|
||||||
|
- new_instance_attrs,
|
||||||
|
- ),
|
||||||
|
+ lrm_rsc_op_start_list[0], new_instance_attrs_digests
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise LibraryError(
|
||||||
|
@@ -364,7 +483,7 @@ def update_scsi_devices_without_restart(
|
||||||
|
|
||||||
|
monitor_attrs_list = _get_monitor_attrs(resource_el)
|
||||||
|
lrm_rsc_op_monitor_list = _get_lrm_rsc_op_elements(
|
||||||
|
- get_root(resource_el), resource_id, node_name, "monitor"
|
||||||
|
+ cib, resource_id, node_name, "monitor"
|
||||||
|
)
|
||||||
|
if len(lrm_rsc_op_monitor_list) != len(monitor_attrs_list):
|
||||||
|
raise LibraryError(
|
||||||
|
@@ -380,7 +499,7 @@ def update_scsi_devices_without_restart(
|
||||||
|
|
||||||
|
for monitor_attrs in monitor_attrs_list:
|
||||||
|
lrm_rsc_op_list = _get_lrm_rsc_op_elements(
|
||||||
|
- get_root(resource_el),
|
||||||
|
+ cib,
|
||||||
|
resource_id,
|
||||||
|
node_name,
|
||||||
|
"monitor",
|
||||||
|
@@ -409,3 +528,10 @@ def update_scsi_devices_without_restart(
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
+ for nvset_el in _get_transient_instance_attributes(cib):
|
||||||
|
+ _update_digest_attrs_in_transient_instance_attributes(
|
||||||
|
+ nvset_el,
|
||||||
|
+ resource_id,
|
||||||
|
+ resource_el.get("type", ""),
|
||||||
|
+ new_instance_attrs_digests,
|
||||||
|
+ )
|
||||||
|
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 69ea097c..72c7dbcf 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
|
||||||
|
@@ -38,6 +38,7 @@ DEFAULT_DIGEST = _DIGEST + "0"
|
||||||
|
ALL_DIGEST = _DIGEST + "1"
|
||||||
|
NONPRIVATE_DIGEST = _DIGEST + "2"
|
||||||
|
NONRELOADABLE_DIGEST = _DIGEST + "3"
|
||||||
|
+DIGEST_ATTR_VALUE_GOOD_FORMAT = f"stonith_id:stonith_type:{DEFAULT_DIGEST},"
|
||||||
|
DEV_1 = "/dev/sda"
|
||||||
|
DEV_2 = "/dev/sdb"
|
||||||
|
DEV_3 = "/dev/sdc"
|
||||||
|
@@ -151,33 +152,58 @@ def _fixture_lrm_rsc_start_ops(resource_id, lrm_start_ops):
|
||||||
|
return _fixture_lrm_rsc_ops("start", resource_id, lrm_start_ops)
|
||||||
|
|
||||||
|
|
||||||
|
-def _fixture_status_lrm_ops_base(
|
||||||
|
- resource_id,
|
||||||
|
- resource_type,
|
||||||
|
- lrm_ops,
|
||||||
|
-):
|
||||||
|
+def _fixture_status_lrm_ops(resource_id, resource_type, lrm_ops):
|
||||||
|
return f"""
|
||||||
|
- <status>
|
||||||
|
- <node_state id="1" uname="node1">
|
||||||
|
- <lrm id="1">
|
||||||
|
- <lrm_resources>
|
||||||
|
- <lrm_resource id="{resource_id}" type="{resource_type}" class="stonith">
|
||||||
|
- {lrm_ops}
|
||||||
|
- </lrm_resource>
|
||||||
|
- </lrm_resources>
|
||||||
|
- </lrm>
|
||||||
|
- </node_state>
|
||||||
|
- </status>
|
||||||
|
+ <lrm id="1">
|
||||||
|
+ <lrm_resources>
|
||||||
|
+ <lrm_resource id="{resource_id}" type="{resource_type}" class="stonith">
|
||||||
|
+ {lrm_ops}
|
||||||
|
+ </lrm_resource>
|
||||||
|
+ </lrm_resources>
|
||||||
|
+ </lrm>
|
||||||
|
+ """
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def _fixture_digest_nvpair(node_id, digest_name, digest_value):
|
||||||
|
+ return (
|
||||||
|
+ f'<nvpair id="status-{node_id}-.{digest_name}" name="#{digest_name}" '
|
||||||
|
+ f'value="{digest_value}"/>'
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def _fixture_transient_attributes(node_id, digests_nvpairs):
|
||||||
|
+ return f"""
|
||||||
|
+ <transient_attributes id="{node_id}">
|
||||||
|
+ <instance_attributes id="status-{node_id}">
|
||||||
|
+ <nvpair id="status-{node_id}-.feature-set" name="#feature-set" value="3.16.2"/>
|
||||||
|
+ <nvpair id="status-{node_id}-.node-unfenced" name="#node-unfenced" value="1679319764"/>
|
||||||
|
+ {digests_nvpairs}
|
||||||
|
+ </instance_attributes>
|
||||||
|
+ </transient_attributes>
|
||||||
|
+ """
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def _fixture_node_state(node_id, lrm_ops=None, transient_attrs=None):
|
||||||
|
+ if transient_attrs is None:
|
||||||
|
+ transient_attrs = ""
|
||||||
|
+ if lrm_ops is None:
|
||||||
|
+ lrm_ops = ""
|
||||||
|
+ return f"""
|
||||||
|
+ <node_state id="{node_id}" uname="node{node_id}">
|
||||||
|
+ {lrm_ops}
|
||||||
|
+ {transient_attrs}
|
||||||
|
+ </node_state>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
-def _fixture_status_lrm_ops(
|
||||||
|
+def _fixture_status(
|
||||||
|
resource_id,
|
||||||
|
resource_type,
|
||||||
|
lrm_start_ops=DEFAULT_LRM_START_OPS,
|
||||||
|
lrm_monitor_ops=DEFAULT_LRM_MONITOR_OPS,
|
||||||
|
+ digests_attrs_list=None,
|
||||||
|
):
|
||||||
|
- return _fixture_status_lrm_ops_base(
|
||||||
|
+ lrm_ops = _fixture_status_lrm_ops(
|
||||||
|
resource_id,
|
||||||
|
resource_type,
|
||||||
|
"\n".join(
|
||||||
|
@@ -185,18 +211,52 @@ def _fixture_status_lrm_ops(
|
||||||
|
+ _fixture_lrm_rsc_monitor_ops(resource_id, lrm_monitor_ops)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
+ node_states_list = []
|
||||||
|
+ if not digests_attrs_list:
|
||||||
|
+ node_states_list.append(
|
||||||
|
+ _fixture_node_state("1", lrm_ops, transient_attrs=None)
|
||||||
|
+ )
|
||||||
|
+ else:
|
||||||
|
+ for node_id, digests_attrs in enumerate(digests_attrs_list, start=1):
|
||||||
|
+ transient_attrs = _fixture_transient_attributes(
|
||||||
|
+ node_id,
|
||||||
|
+ "\n".join(
|
||||||
|
+ _fixture_digest_nvpair(node_id, name, value)
|
||||||
|
+ for name, value in digests_attrs
|
||||||
|
+ ),
|
||||||
|
+ )
|
||||||
|
+ node_state = _fixture_node_state(
|
||||||
|
+ node_id,
|
||||||
|
+ lrm_ops=lrm_ops if node_id == 1 else None,
|
||||||
|
+ transient_attrs=transient_attrs,
|
||||||
|
+ )
|
||||||
|
+ node_states_list.append(node_state)
|
||||||
|
+ node_states = "\n".join(node_states_list)
|
||||||
|
+ return f"""
|
||||||
|
+ <status>
|
||||||
|
+ {node_states}
|
||||||
|
+ </status>
|
||||||
|
+ """
|
||||||
|
+
|
||||||
|
|
||||||
|
+def fixture_digests_xml(resource_id, node_name, devices="", nonprivate=True):
|
||||||
|
+ nonprivate_xml = (
|
||||||
|
+ f"""
|
||||||
|
+ <digest type="nonprivate" hash="{NONPRIVATE_DIGEST}">
|
||||||
|
+ <parameters devices="{devices}"/>
|
||||||
|
+ </digest>
|
||||||
|
+ """
|
||||||
|
+ if nonprivate
|
||||||
|
+ else ""
|
||||||
|
+ )
|
||||||
|
|
||||||
|
-def fixture_digests_xml(resource_id, node_name, devices=""):
|
||||||
|
return f"""
|
||||||
|
<pacemaker-result api-version="2.9" request="crm_resource --digests --resource {resource_id} --node {node_name} --output-as xml devices={devices}">
|
||||||
|
<digests resource="{resource_id}" node="{node_name}" task="stop" interval="0ms">
|
||||||
|
<digest type="all" hash="{ALL_DIGEST}">
|
||||||
|
<parameters devices="{devices}" pcmk_host_check="static-list" pcmk_host_list="node1 node2 node3" pcmk_reboot_action="off"/>
|
||||||
|
</digest>
|
||||||
|
- <digest type="nonprivate" hash="{NONPRIVATE_DIGEST}">
|
||||||
|
- <parameters devices="{devices}"/>
|
||||||
|
- </digest>
|
||||||
|
+ {nonprivate_xml}
|
||||||
|
</digests>
|
||||||
|
<status code="0" message="OK"/>
|
||||||
|
</pacemaker-result>
|
||||||
|
@@ -334,6 +394,8 @@ class UpdateScsiDevicesMixin:
|
||||||
|
nodes_running_on=1,
|
||||||
|
start_digests=True,
|
||||||
|
monitor_digests=True,
|
||||||
|
+ digests_attrs_list=None,
|
||||||
|
+ crm_digests_xml=None,
|
||||||
|
):
|
||||||
|
# pylint: disable=too-many-arguments
|
||||||
|
# pylint: disable=too-many-locals
|
||||||
|
@@ -346,11 +408,12 @@ class UpdateScsiDevicesMixin:
|
||||||
|
resource_ops=resource_ops,
|
||||||
|
host_map=host_map,
|
||||||
|
),
|
||||||
|
- status=_fixture_status_lrm_ops(
|
||||||
|
+ status=_fixture_status(
|
||||||
|
self.stonith_id,
|
||||||
|
self.stonith_type,
|
||||||
|
lrm_start_ops=lrm_start_ops,
|
||||||
|
lrm_monitor_ops=lrm_monitor_ops,
|
||||||
|
+ digests_attrs_list=digests_attrs_list,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
self.config.runner.pcmk.is_resource_digests_supported()
|
||||||
|
@@ -363,14 +426,17 @@ class UpdateScsiDevicesMixin:
|
||||||
|
nodes=FIXTURE_CRM_MON_NODES,
|
||||||
|
)
|
||||||
|
devices_opt = "devices={}".format(devices_value)
|
||||||
|
+
|
||||||
|
+ if crm_digests_xml is None:
|
||||||
|
+ crm_digests_xml = fixture_digests_xml(
|
||||||
|
+ self.stonith_id, SCSI_NODE, devices=devices_value
|
||||||
|
+ )
|
||||||
|
if start_digests:
|
||||||
|
self.config.runner.pcmk.resource_digests(
|
||||||
|
self.stonith_id,
|
||||||
|
SCSI_NODE,
|
||||||
|
name="start.op.digests",
|
||||||
|
- stdout=fixture_digests_xml(
|
||||||
|
- self.stonith_id, SCSI_NODE, devices=devices_value
|
||||||
|
- ),
|
||||||
|
+ stdout=crm_digests_xml,
|
||||||
|
args=[devices_opt],
|
||||||
|
)
|
||||||
|
if monitor_digests:
|
||||||
|
@@ -394,11 +460,7 @@ class UpdateScsiDevicesMixin:
|
||||||
|
self.stonith_id,
|
||||||
|
SCSI_NODE,
|
||||||
|
name=f"{name}-{num}.op.digests",
|
||||||
|
- stdout=fixture_digests_xml(
|
||||||
|
- self.stonith_id,
|
||||||
|
- SCSI_NODE,
|
||||||
|
- devices=devices_value,
|
||||||
|
- ),
|
||||||
|
+ stdout=crm_digests_xml,
|
||||||
|
args=args,
|
||||||
|
)
|
||||||
|
|
||||||
|
@@ -406,14 +468,16 @@ class UpdateScsiDevicesMixin:
|
||||||
|
self,
|
||||||
|
devices_before=DEVICES_1,
|
||||||
|
devices_updated=DEVICES_2,
|
||||||
|
- devices_add=(),
|
||||||
|
- devices_remove=(),
|
||||||
|
+ devices_add=None,
|
||||||
|
+ devices_remove=None,
|
||||||
|
unfence=None,
|
||||||
|
resource_ops=DEFAULT_OPS,
|
||||||
|
lrm_monitor_ops=DEFAULT_LRM_MONITOR_OPS,
|
||||||
|
lrm_start_ops=DEFAULT_LRM_START_OPS,
|
||||||
|
lrm_monitor_ops_updated=DEFAULT_LRM_MONITOR_OPS_UPDATED,
|
||||||
|
lrm_start_ops_updated=DEFAULT_LRM_START_OPS_UPDATED,
|
||||||
|
+ digests_attrs_list=None,
|
||||||
|
+ digests_attrs_list_updated=None,
|
||||||
|
):
|
||||||
|
# pylint: disable=too-many-arguments
|
||||||
|
self.config_cib(
|
||||||
|
@@ -422,6 +486,7 @@ class UpdateScsiDevicesMixin:
|
||||||
|
resource_ops=resource_ops,
|
||||||
|
lrm_monitor_ops=lrm_monitor_ops,
|
||||||
|
lrm_start_ops=lrm_start_ops,
|
||||||
|
+ digests_attrs_list=digests_attrs_list,
|
||||||
|
)
|
||||||
|
if unfence:
|
||||||
|
self.config.corosync_conf.load_content(
|
||||||
|
@@ -445,20 +510,34 @@ class UpdateScsiDevicesMixin:
|
||||||
|
devices=devices_updated,
|
||||||
|
resource_ops=resource_ops,
|
||||||
|
),
|
||||||
|
- status=_fixture_status_lrm_ops(
|
||||||
|
+ status=_fixture_status(
|
||||||
|
self.stonith_id,
|
||||||
|
self.stonith_type,
|
||||||
|
lrm_start_ops=lrm_start_ops_updated,
|
||||||
|
lrm_monitor_ops=lrm_monitor_ops_updated,
|
||||||
|
+ digests_attrs_list=digests_attrs_list_updated,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
- self.command(
|
||||||
|
- devices_updated=devices_updated,
|
||||||
|
- devices_add=devices_add,
|
||||||
|
- devices_remove=devices_remove,
|
||||||
|
- )()
|
||||||
|
+ kwargs = dict(devices_updated=devices_updated)
|
||||||
|
+ if devices_add is not None:
|
||||||
|
+ kwargs["devices_add"] = devices_add
|
||||||
|
+ if devices_remove is not None:
|
||||||
|
+ kwargs["devices_remove"] = devices_remove
|
||||||
|
+ self.command(**kwargs)()
|
||||||
|
self.env_assist.assert_reports([])
|
||||||
|
|
||||||
|
+ def digest_attr_value_single(self, digest, last_comma=True):
|
||||||
|
+ comma = "," if last_comma else ""
|
||||||
|
+ return f"{self.stonith_id}:{self.stonith_type}:{digest}{comma}"
|
||||||
|
+
|
||||||
|
+ def digest_attr_value_multiple(self, digest, last_comma=True):
|
||||||
|
+ if self.stonith_type == STONITH_TYPE_SCSI:
|
||||||
|
+ value = f"{STONITH_ID_MPATH}:{STONITH_TYPE_MPATH}:{DEFAULT_DIGEST},"
|
||||||
|
+ else:
|
||||||
|
+ value = f"{STONITH_ID_SCSI}:{STONITH_TYPE_SCSI}:{DEFAULT_DIGEST},"
|
||||||
|
+
|
||||||
|
+ return f"{value}{self.digest_attr_value_single(digest, last_comma=last_comma)}"
|
||||||
|
+
|
||||||
|
|
||||||
|
class UpdateScsiDevicesFailuresMixin(UpdateScsiDevicesMixin):
|
||||||
|
def test_pcmk_doesnt_support_digests(self):
|
||||||
|
@@ -567,9 +646,7 @@ class UpdateScsiDevicesFailuresMixin(UpdateScsiDevicesMixin):
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_no_lrm_start_op(self):
|
||||||
|
- self.config_cib(
|
||||||
|
- lrm_start_ops=(), start_digests=False, monitor_digests=False
|
||||||
|
- )
|
||||||
|
+ self.config_cib(lrm_start_ops=(), monitor_digests=False)
|
||||||
|
self.env_assist.assert_raise_library_error(
|
||||||
|
self.command(),
|
||||||
|
[
|
||||||
|
@@ -622,6 +699,59 @@ class UpdateScsiDevicesFailuresMixin(UpdateScsiDevicesMixin):
|
||||||
|
expected_in_processor=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
+ def test_crm_resource_digests_missing_for_transient_digests_attrs(self):
|
||||||
|
+ self.config_cib(
|
||||||
|
+ digests_attrs_list=[
|
||||||
|
+ [
|
||||||
|
+ (
|
||||||
|
+ "digests-secure",
|
||||||
|
+ self.digest_attr_value_single(ALL_DIGEST),
|
||||||
|
+ ),
|
||||||
|
+ ],
|
||||||
|
+ ],
|
||||||
|
+ crm_digests_xml=fixture_digests_xml(
|
||||||
|
+ self.stonith_id, SCSI_NODE, devices="", nonprivate=False
|
||||||
|
+ ),
|
||||||
|
+ )
|
||||||
|
+ self.env_assist.assert_raise_library_error(
|
||||||
|
+ self.command(),
|
||||||
|
+ [
|
||||||
|
+ fixture.error(
|
||||||
|
+ reports.codes.STONITH_RESTARTLESS_UPDATE_UNABLE_TO_PERFORM,
|
||||||
|
+ reason=(
|
||||||
|
+ "necessary digest for '#digests-secure' attribute is "
|
||||||
|
+ "missing"
|
||||||
|
+ ),
|
||||||
|
+ reason_type=reports.const.STONITH_RESTARTLESS_UPDATE_UNABLE_TO_PERFORM_REASON_OTHER,
|
||||||
|
+ )
|
||||||
|
+ ],
|
||||||
|
+ expected_in_processor=False,
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ def test_multiple_digests_attributes(self):
|
||||||
|
+ self.config_cib(
|
||||||
|
+ digests_attrs_list=[
|
||||||
|
+ 2
|
||||||
|
+ * [
|
||||||
|
+ (
|
||||||
|
+ "digests-all",
|
||||||
|
+ self.digest_attr_value_single(DEFAULT_DIGEST),
|
||||||
|
+ ),
|
||||||
|
+ ],
|
||||||
|
+ ],
|
||||||
|
+ )
|
||||||
|
+ self.env_assist.assert_raise_library_error(
|
||||||
|
+ self.command(),
|
||||||
|
+ [
|
||||||
|
+ fixture.error(
|
||||||
|
+ reports.codes.STONITH_RESTARTLESS_UPDATE_UNABLE_TO_PERFORM,
|
||||||
|
+ reason=("multiple digests attributes: '#digests-all'"),
|
||||||
|
+ reason_type=reports.const.STONITH_RESTARTLESS_UPDATE_UNABLE_TO_PERFORM_REASON_OTHER,
|
||||||
|
+ )
|
||||||
|
+ ],
|
||||||
|
+ expected_in_processor=False,
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
def test_monitor_ops_and_lrm_monitor_ops_do_not_match(self):
|
||||||
|
self.config_cib(
|
||||||
|
resource_ops=(
|
||||||
|
@@ -812,7 +942,7 @@ class UpdateScsiDevicesFailuresMixin(UpdateScsiDevicesMixin):
|
||||||
|
stonith_type=self.stonith_type,
|
||||||
|
devices=DEVICES_2,
|
||||||
|
),
|
||||||
|
- status=_fixture_status_lrm_ops(
|
||||||
|
+ status=_fixture_status(
|
||||||
|
self.stonith_id,
|
||||||
|
self.stonith_type,
|
||||||
|
lrm_start_ops=DEFAULT_LRM_START_OPS_UPDATED,
|
||||||
|
@@ -959,6 +1089,28 @@ class UpdateScsiDevicesFailuresMixin(UpdateScsiDevicesMixin):
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
+ def test_transient_digests_attrs_bad_value_format(self):
|
||||||
|
+ bad_format = f"{DIGEST_ATTR_VALUE_GOOD_FORMAT}id:type,"
|
||||||
|
+ self.config_cib(
|
||||||
|
+ digests_attrs_list=[
|
||||||
|
+ [
|
||||||
|
+ ("digests-all", DIGEST_ATTR_VALUE_GOOD_FORMAT),
|
||||||
|
+ ("digests-secure", bad_format),
|
||||||
|
+ ]
|
||||||
|
+ ]
|
||||||
|
+ )
|
||||||
|
+ self.env_assist.assert_raise_library_error(
|
||||||
|
+ self.command(),
|
||||||
|
+ [
|
||||||
|
+ fixture.error(
|
||||||
|
+ reports.codes.STONITH_RESTARTLESS_UPDATE_UNABLE_TO_PERFORM,
|
||||||
|
+ reason=f"invalid digest attribute value: '{bad_format}'",
|
||||||
|
+ reason_type=reports.const.STONITH_RESTARTLESS_UPDATE_UNABLE_TO_PERFORM_REASON_OTHER,
|
||||||
|
+ )
|
||||||
|
+ ],
|
||||||
|
+ expected_in_processor=False,
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
|
||||||
|
class UpdateScsiDevicesSetBase(UpdateScsiDevicesMixin, CommandSetMixin):
|
||||||
|
def test_update_1_to_1_devices(self):
|
||||||
|
@@ -1002,80 +1154,6 @@ class UpdateScsiDevicesSetBase(UpdateScsiDevicesMixin, CommandSetMixin):
|
||||||
|
unfence=[DEV_3, DEV_4],
|
||||||
|
)
|
||||||
|
|
||||||
|
- def test_default_monitor(self):
|
||||||
|
- self.assert_command_success(unfence=[DEV_2])
|
||||||
|
-
|
||||||
|
- def test_no_monitor_ops(self):
|
||||||
|
- self.assert_command_success(
|
||||||
|
- unfence=[DEV_2],
|
||||||
|
- resource_ops=(),
|
||||||
|
- lrm_monitor_ops=(),
|
||||||
|
- lrm_monitor_ops_updated=(),
|
||||||
|
- )
|
||||||
|
-
|
||||||
|
- def test_1_monitor_with_timeout(self):
|
||||||
|
- self.assert_command_success(
|
||||||
|
- unfence=[DEV_2],
|
||||||
|
- resource_ops=(("monitor", "30s", "10s", None),),
|
||||||
|
- lrm_monitor_ops=(("30000", DEFAULT_DIGEST, None, None),),
|
||||||
|
- lrm_monitor_ops_updated=(("30000", ALL_DIGEST, None, None),),
|
||||||
|
- )
|
||||||
|
-
|
||||||
|
- def test_2_monitor_ops_with_timeouts(self):
|
||||||
|
- self.assert_command_success(
|
||||||
|
- unfence=[DEV_2],
|
||||||
|
- resource_ops=(
|
||||||
|
- ("monitor", "30s", "10s", None),
|
||||||
|
- ("monitor", "40s", "20s", None),
|
||||||
|
- ),
|
||||||
|
- lrm_monitor_ops=(
|
||||||
|
- ("30000", DEFAULT_DIGEST, None, None),
|
||||||
|
- ("40000", DEFAULT_DIGEST, None, None),
|
||||||
|
- ),
|
||||||
|
- lrm_monitor_ops_updated=(
|
||||||
|
- ("30000", ALL_DIGEST, None, None),
|
||||||
|
- ("40000", ALL_DIGEST, None, None),
|
||||||
|
- ),
|
||||||
|
- )
|
||||||
|
-
|
||||||
|
- def test_2_monitor_ops_with_one_timeout(self):
|
||||||
|
- self.assert_command_success(
|
||||||
|
- unfence=[DEV_2],
|
||||||
|
- resource_ops=(
|
||||||
|
- ("monitor", "30s", "10s", None),
|
||||||
|
- ("monitor", "60s", None, None),
|
||||||
|
- ),
|
||||||
|
- lrm_monitor_ops=(
|
||||||
|
- ("30000", DEFAULT_DIGEST, None, None),
|
||||||
|
- ("60000", DEFAULT_DIGEST, None, None),
|
||||||
|
- ),
|
||||||
|
- lrm_monitor_ops_updated=(
|
||||||
|
- ("30000", ALL_DIGEST, None, None),
|
||||||
|
- ("60000", ALL_DIGEST, None, None),
|
||||||
|
- ),
|
||||||
|
- )
|
||||||
|
-
|
||||||
|
- def test_various_start_ops_one_lrm_start_op(self):
|
||||||
|
- self.assert_command_success(
|
||||||
|
- unfence=[DEV_2],
|
||||||
|
- resource_ops=(
|
||||||
|
- ("monitor", "60s", None, None),
|
||||||
|
- ("start", "0s", "40s", None),
|
||||||
|
- ("start", "0s", "30s", "1"),
|
||||||
|
- ("start", "10s", "5s", None),
|
||||||
|
- ("start", "20s", None, None),
|
||||||
|
- ),
|
||||||
|
- )
|
||||||
|
-
|
||||||
|
- def test_1_nonrecurring_start_op_with_timeout(self):
|
||||||
|
- self.assert_command_success(
|
||||||
|
- unfence=[DEV_2],
|
||||||
|
- resource_ops=(
|
||||||
|
- ("monitor", "60s", None, None),
|
||||||
|
- ("start", "0s", "40s", None),
|
||||||
|
- ),
|
||||||
|
- )
|
||||||
|
-
|
||||||
|
|
||||||
|
class UpdateScsiDevicesAddRemoveBase(
|
||||||
|
UpdateScsiDevicesMixin, CommandAddRemoveMixin
|
||||||
|
@@ -1245,6 +1323,221 @@ class MpathFailuresMixin:
|
||||||
|
self.assert_failure("node1:1;node2=", ["node2", "node3"])
|
||||||
|
|
||||||
|
|
||||||
|
+class UpdateScsiDevicesDigestsBase(UpdateScsiDevicesMixin):
|
||||||
|
+ def test_default_monitor(self):
|
||||||
|
+ self.assert_command_success(unfence=[DEV_2])
|
||||||
|
+
|
||||||
|
+ def test_no_monitor_ops(self):
|
||||||
|
+ self.assert_command_success(
|
||||||
|
+ unfence=[DEV_2],
|
||||||
|
+ resource_ops=(),
|
||||||
|
+ lrm_monitor_ops=(),
|
||||||
|
+ lrm_monitor_ops_updated=(),
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ def test_1_monitor_with_timeout(self):
|
||||||
|
+ self.assert_command_success(
|
||||||
|
+ unfence=[DEV_2],
|
||||||
|
+ resource_ops=(("monitor", "30s", "10s", None),),
|
||||||
|
+ lrm_monitor_ops=(("30000", DEFAULT_DIGEST, None, None),),
|
||||||
|
+ lrm_monitor_ops_updated=(("30000", ALL_DIGEST, None, None),),
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ def test_2_monitor_ops_with_timeouts(self):
|
||||||
|
+ self.assert_command_success(
|
||||||
|
+ unfence=[DEV_2],
|
||||||
|
+ resource_ops=(
|
||||||
|
+ ("monitor", "30s", "10s", None),
|
||||||
|
+ ("monitor", "40s", "20s", None),
|
||||||
|
+ ),
|
||||||
|
+ lrm_monitor_ops=(
|
||||||
|
+ ("30000", DEFAULT_DIGEST, None, None),
|
||||||
|
+ ("40000", DEFAULT_DIGEST, None, None),
|
||||||
|
+ ),
|
||||||
|
+ lrm_monitor_ops_updated=(
|
||||||
|
+ ("30000", ALL_DIGEST, None, None),
|
||||||
|
+ ("40000", ALL_DIGEST, None, None),
|
||||||
|
+ ),
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ def test_2_monitor_ops_with_one_timeout(self):
|
||||||
|
+ self.assert_command_success(
|
||||||
|
+ unfence=[DEV_2],
|
||||||
|
+ resource_ops=(
|
||||||
|
+ ("monitor", "30s", "10s", None),
|
||||||
|
+ ("monitor", "60s", None, None),
|
||||||
|
+ ),
|
||||||
|
+ lrm_monitor_ops=(
|
||||||
|
+ ("30000", DEFAULT_DIGEST, None, None),
|
||||||
|
+ ("60000", DEFAULT_DIGEST, None, None),
|
||||||
|
+ ),
|
||||||
|
+ lrm_monitor_ops_updated=(
|
||||||
|
+ ("30000", ALL_DIGEST, None, None),
|
||||||
|
+ ("60000", ALL_DIGEST, None, None),
|
||||||
|
+ ),
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ def test_various_start_ops_one_lrm_start_op(self):
|
||||||
|
+ self.assert_command_success(
|
||||||
|
+ unfence=[DEV_2],
|
||||||
|
+ resource_ops=(
|
||||||
|
+ ("monitor", "60s", None, None),
|
||||||
|
+ ("start", "0s", "40s", None),
|
||||||
|
+ ("start", "0s", "30s", "1"),
|
||||||
|
+ ("start", "10s", "5s", None),
|
||||||
|
+ ("start", "20s", None, None),
|
||||||
|
+ ),
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ def test_1_nonrecurring_start_op_with_timeout(self):
|
||||||
|
+ self.assert_command_success(
|
||||||
|
+ unfence=[DEV_2],
|
||||||
|
+ resource_ops=(
|
||||||
|
+ ("monitor", "60s", None, None),
|
||||||
|
+ ("start", "0s", "40s", None),
|
||||||
|
+ ),
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ def _digests_attrs_before(self, last_comma=True):
|
||||||
|
+ return [
|
||||||
|
+ (
|
||||||
|
+ "digests-all",
|
||||||
|
+ self.digest_attr_value_single(DEFAULT_DIGEST, last_comma),
|
||||||
|
+ ),
|
||||||
|
+ (
|
||||||
|
+ "digests-secure",
|
||||||
|
+ self.digest_attr_value_single(DEFAULT_DIGEST, last_comma),
|
||||||
|
+ ),
|
||||||
|
+ ]
|
||||||
|
+
|
||||||
|
+ def _digests_attrs_after(self, last_comma=True):
|
||||||
|
+ return [
|
||||||
|
+ (
|
||||||
|
+ "digests-all",
|
||||||
|
+ self.digest_attr_value_single(ALL_DIGEST, last_comma),
|
||||||
|
+ ),
|
||||||
|
+ (
|
||||||
|
+ "digests-secure",
|
||||||
|
+ self.digest_attr_value_single(NONPRIVATE_DIGEST, last_comma),
|
||||||
|
+ ),
|
||||||
|
+ ]
|
||||||
|
+
|
||||||
|
+ def _digests_attrs_before_multi(self, last_comma=True):
|
||||||
|
+ return [
|
||||||
|
+ (
|
||||||
|
+ "digests-all",
|
||||||
|
+ self.digest_attr_value_multiple(DEFAULT_DIGEST, last_comma),
|
||||||
|
+ ),
|
||||||
|
+ (
|
||||||
|
+ "digests-secure",
|
||||||
|
+ self.digest_attr_value_multiple(DEFAULT_DIGEST, last_comma),
|
||||||
|
+ ),
|
||||||
|
+ ]
|
||||||
|
+
|
||||||
|
+ def _digests_attrs_after_multi(self, last_comma=True):
|
||||||
|
+ return [
|
||||||
|
+ (
|
||||||
|
+ "digests-all",
|
||||||
|
+ self.digest_attr_value_multiple(ALL_DIGEST, last_comma),
|
||||||
|
+ ),
|
||||||
|
+ (
|
||||||
|
+ "digests-secure",
|
||||||
|
+ self.digest_attr_value_multiple(NONPRIVATE_DIGEST, last_comma),
|
||||||
|
+ ),
|
||||||
|
+ ]
|
||||||
|
+
|
||||||
|
+ def test_transient_digests_attrs_all_nodes(self):
|
||||||
|
+ self.assert_command_success(
|
||||||
|
+ unfence=[DEV_2],
|
||||||
|
+ digests_attrs_list=len(self.existing_nodes)
|
||||||
|
+ * [self._digests_attrs_before()],
|
||||||
|
+ digests_attrs_list_updated=len(self.existing_nodes)
|
||||||
|
+ * [self._digests_attrs_after()],
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ def test_transient_digests_attrs_not_on_all_nodes(self):
|
||||||
|
+ self.assert_command_success(
|
||||||
|
+ unfence=[DEV_2],
|
||||||
|
+ digests_attrs_list=[self._digests_attrs_before()],
|
||||||
|
+ digests_attrs_list_updated=[self._digests_attrs_after()],
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ def test_transient_digests_attrs_all_nodes_multi_value(self):
|
||||||
|
+ self.assert_command_success(
|
||||||
|
+ unfence=[DEV_2],
|
||||||
|
+ digests_attrs_list=len(self.existing_nodes)
|
||||||
|
+ * [self._digests_attrs_before_multi()],
|
||||||
|
+ digests_attrs_list_updated=len(self.existing_nodes)
|
||||||
|
+ * [self._digests_attrs_after_multi()],
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ def test_transient_digests_attrs_not_on_all_nodes_multi_value(self):
|
||||||
|
+ self.assert_command_success(
|
||||||
|
+ unfence=[DEV_2],
|
||||||
|
+ digests_attrs_list=[self._digests_attrs_before()],
|
||||||
|
+ digests_attrs_list_updated=[self._digests_attrs_after()],
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ def test_transient_digests_attrs_not_all_digest_types(self):
|
||||||
|
+ self.assert_command_success(
|
||||||
|
+ unfence=[DEV_2],
|
||||||
|
+ digests_attrs_list=len(self.existing_nodes)
|
||||||
|
+ * [self._digests_attrs_before()[0:1]],
|
||||||
|
+ digests_attrs_list_updated=len(self.existing_nodes)
|
||||||
|
+ * [self._digests_attrs_after()[0:1]],
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ def test_transient_digests_attrs_without_digests_attrs(self):
|
||||||
|
+ self.assert_command_success(
|
||||||
|
+ unfence=[DEV_2],
|
||||||
|
+ digests_attrs_list=len(self.existing_nodes) * [[]],
|
||||||
|
+ digests_attrs_list_updated=len(self.existing_nodes) * [[]],
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ def test_transient_digests_attrs_without_last_comma(self):
|
||||||
|
+ self.assert_command_success(
|
||||||
|
+ unfence=[DEV_2],
|
||||||
|
+ digests_attrs_list=[self._digests_attrs_before(last_comma=False)],
|
||||||
|
+ digests_attrs_list_updated=[
|
||||||
|
+ self._digests_attrs_after(last_comma=False)
|
||||||
|
+ ],
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ def test_transient_digests_attrs_without_last_comma_multi_value(self):
|
||||||
|
+ self.assert_command_success(
|
||||||
|
+ unfence=[DEV_2],
|
||||||
|
+ digests_attrs_list=[
|
||||||
|
+ self._digests_attrs_before_multi(last_comma=False)
|
||||||
|
+ ],
|
||||||
|
+ digests_attrs_list_updated=[
|
||||||
|
+ self._digests_attrs_after_multi(last_comma=False)
|
||||||
|
+ ],
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ def test_transient_digests_attrs_no_digest_for_our_stonith_id(self):
|
||||||
|
+ digests_attrs_list = len(self.existing_nodes) * [
|
||||||
|
+ [
|
||||||
|
+ ("digests-all", DIGEST_ATTR_VALUE_GOOD_FORMAT),
|
||||||
|
+ ("digests-secure", DIGEST_ATTR_VALUE_GOOD_FORMAT),
|
||||||
|
+ ]
|
||||||
|
+ ]
|
||||||
|
+ self.assert_command_success(
|
||||||
|
+ unfence=[DEV_2],
|
||||||
|
+ digests_attrs_list=digests_attrs_list,
|
||||||
|
+ digests_attrs_list_updated=digests_attrs_list,
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ def test_transient_digests_attrs_digests_with_empty_value(self):
|
||||||
|
+ digests_attrs_list = len(self.existing_nodes) * [
|
||||||
|
+ [("digests-all", ""), ("digests-secure", "")]
|
||||||
|
+ ]
|
||||||
|
+ self.assert_command_success(
|
||||||
|
+ unfence=[DEV_2],
|
||||||
|
+ digests_attrs_list=digests_attrs_list,
|
||||||
|
+ digests_attrs_list_updated=digests_attrs_list,
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+
|
||||||
|
@mock.patch.object(
|
||||||
|
settings,
|
||||||
|
"pacemaker_api_result_schema",
|
||||||
|
@@ -1337,3 +1630,47 @@ class TestUpdateScsiDevicesAddRemoveFailuresScsi(
|
||||||
|
UpdateScsiDevicesAddRemoveFailuresBaseMixin, ScsiMixin, TestCase
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+@mock.patch.object(
|
||||||
|
+ settings,
|
||||||
|
+ "pacemaker_api_result_schema",
|
||||||
|
+ rc("pcmk_api_rng/api-result.rng"),
|
||||||
|
+)
|
||||||
|
+class TestUpdateScsiDevicesDigestsSetScsi(
|
||||||
|
+ UpdateScsiDevicesDigestsBase, ScsiMixin, CommandSetMixin, TestCase
|
||||||
|
+):
|
||||||
|
+ pass
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+@mock.patch.object(
|
||||||
|
+ settings,
|
||||||
|
+ "pacemaker_api_result_schema",
|
||||||
|
+ rc("pcmk_api_rng/api-result.rng"),
|
||||||
|
+)
|
||||||
|
+class TestUpdateScsiDevicesDigestsAddRemoveScsi(
|
||||||
|
+ UpdateScsiDevicesDigestsBase, ScsiMixin, CommandAddRemoveMixin, TestCase
|
||||||
|
+):
|
||||||
|
+ pass
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+@mock.patch.object(
|
||||||
|
+ settings,
|
||||||
|
+ "pacemaker_api_result_schema",
|
||||||
|
+ rc("pcmk_api_rng/api-result.rng"),
|
||||||
|
+)
|
||||||
|
+class TestUpdateScsiDevicesDigestsSetMpath(
|
||||||
|
+ UpdateScsiDevicesDigestsBase, MpathMixin, CommandSetMixin, TestCase
|
||||||
|
+):
|
||||||
|
+ pass
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+@mock.patch.object(
|
||||||
|
+ settings,
|
||||||
|
+ "pacemaker_api_result_schema",
|
||||||
|
+ rc("pcmk_api_rng/api-result.rng"),
|
||||||
|
+)
|
||||||
|
+class TestUpdateScsiDevicesDigestsAddRemoveMpath(
|
||||||
|
+ UpdateScsiDevicesDigestsBase, MpathMixin, CommandAddRemoveMixin, TestCase
|
||||||
|
+):
|
||||||
|
+ pass
|
||||||
|
--
|
||||||
|
2.39.2
|
||||||
|
|
89
bz2183180-01-fix-loading-with-fence-levels.patch
Normal file
89
bz2183180-01-fix-loading-with-fence-levels.patch
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
From 2403a2414f234a4025055e56f8202094caf1b655 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Ivan Devat <idevat@redhat.com>
|
||||||
|
Date: Thu, 30 Mar 2023 17:03:06 +0200
|
||||||
|
Subject: [PATCH] fix cluster-status/fence_levels shape expectation
|
||||||
|
|
||||||
|
---
|
||||||
|
jest.config.js | 1 +
|
||||||
|
.../endpoints/clusterStatus/shape/cluster.ts | 10 +++--
|
||||||
|
.../cluster/displayAdvancedStatus.test.ts | 37 +++++++++++++++++++
|
||||||
|
3 files changed, 44 insertions(+), 4 deletions(-)
|
||||||
|
create mode 100644 src/test/scenes/cluster/displayAdvancedStatus.test.ts
|
||||||
|
|
||||||
|
diff --git a/jest.config.js b/jest.config.js
|
||||||
|
index 08660443..c5c39dc5 100644
|
||||||
|
--- a/jest.config.js
|
||||||
|
+++ b/jest.config.js
|
||||||
|
@@ -1,4 +1,5 @@
|
||||||
|
module.exports = {
|
||||||
|
globalSetup: "./src/test/jest-preset.ts",
|
||||||
|
moduleDirectories: ["node_modules", "src"],
|
||||||
|
+ testTimeout: 10000,
|
||||||
|
};
|
||||||
|
diff --git a/src/app/backend/endpoints/clusterStatus/shape/cluster.ts b/src/app/backend/endpoints/clusterStatus/shape/cluster.ts
|
||||||
|
index 97ec4f17..ea29470e 100644
|
||||||
|
--- a/src/app/backend/endpoints/clusterStatus/shape/cluster.ts
|
||||||
|
+++ b/src/app/backend/endpoints/clusterStatus/shape/cluster.ts
|
||||||
|
@@ -13,10 +13,12 @@ The key of record is a target.
|
||||||
|
*/
|
||||||
|
const ApiFencingLevels = t.record(
|
||||||
|
t.string,
|
||||||
|
- t.type({
|
||||||
|
- level: t.string,
|
||||||
|
- devices: t.array(t.string),
|
||||||
|
- }),
|
||||||
|
+ t.array(
|
||||||
|
+ t.type({
|
||||||
|
+ level: t.string,
|
||||||
|
+ devices: t.string,
|
||||||
|
+ }),
|
||||||
|
+ ),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const ApiClusterStatusFlag = t.keyof({
|
||||||
|
diff --git a/src/test/scenes/cluster/displayAdvancedStatus.test.ts b/src/test/scenes/cluster/displayAdvancedStatus.test.ts
|
||||||
|
new file mode 100644
|
||||||
|
index 00000000..78eb7dbe
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/src/test/scenes/cluster/displayAdvancedStatus.test.ts
|
||||||
|
@@ -0,0 +1,37 @@
|
||||||
|
+// Cluster status is pretty complex. Sometimes a discrepancy between frontend
|
||||||
|
+// and backend appears. This modules collect tests for discovered cases.
|
||||||
|
+
|
||||||
|
+import * as t from "dev/responses/clusterStatus/tools";
|
||||||
|
+
|
||||||
|
+import {dt} from "test/tools/selectors";
|
||||||
|
+import {location, shortcuts} from "test/tools";
|
||||||
|
+
|
||||||
|
+const clusterName = "test-cluster";
|
||||||
|
+
|
||||||
|
+// We want to see browser behavior with (for now) invalid status before fix. But
|
||||||
|
+// the typecheck tell us that it is wrong and dev build fails. So, we decive it.
|
||||||
|
+const deceiveTypeCheck = (maybeInvalidPart: ReturnType<typeof JSON.parse>) =>
|
||||||
|
+ JSON.parse(JSON.stringify(maybeInvalidPart));
|
||||||
|
+
|
||||||
|
+describe("Cluster with advanced status", () => {
|
||||||
|
+ it("accept fence levels", async () => {
|
||||||
|
+ shortcuts.interceptWithCluster({
|
||||||
|
+ clusterStatus: t.cluster(clusterName, "ok", {
|
||||||
|
+ fence_levels: deceiveTypeCheck({
|
||||||
|
+ "node-1": [
|
||||||
|
+ {
|
||||||
|
+ level: "1",
|
||||||
|
+ devices: "fence-1",
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ level: "2",
|
||||||
|
+ devices: "fence-2",
|
||||||
|
+ },
|
||||||
|
+ ],
|
||||||
|
+ }),
|
||||||
|
+ }),
|
||||||
|
+ });
|
||||||
|
+ await page.goto(location.cluster({clusterName}));
|
||||||
|
+ await page.waitForSelector(dt("cluster-overview"));
|
||||||
|
+ });
|
||||||
|
+});
|
||||||
|
--
|
||||||
|
2.39.2
|
||||||
|
|
22
pcs.spec
22
pcs.spec
@ -1,6 +1,6 @@
|
|||||||
Name: pcs
|
Name: pcs
|
||||||
Version: 0.11.4
|
Version: 0.11.4
|
||||||
Release: 6%{?dist}
|
Release: 7%{?dist}
|
||||||
# https://docs.fedoraproject.org/en-US/packaging-guidelines/LicensingGuidelines/
|
# https://docs.fedoraproject.org/en-US/packaging-guidelines/LicensingGuidelines/
|
||||||
# https://fedoraproject.org/wiki/Licensing:Main?rd=Licensing#Good_Licenses
|
# https://fedoraproject.org/wiki/Licensing:Main?rd=Licensing#Good_Licenses
|
||||||
# GPL-2.0-only: pcs
|
# GPL-2.0-only: pcs
|
||||||
@ -24,8 +24,8 @@ ExclusiveArch: i686 x86_64 s390x ppc64le aarch64
|
|||||||
%global pcs_source_name %{name}-%{version_or_commit}
|
%global pcs_source_name %{name}-%{version_or_commit}
|
||||||
|
|
||||||
# ui_commit can be determined by hash, tag or branch
|
# ui_commit can be determined by hash, tag or branch
|
||||||
%global ui_commit 0.1.16
|
%global ui_commit 0.1.16.1
|
||||||
%global ui_modules_version 0.1.16
|
%global ui_modules_version 0.1.16.1
|
||||||
%global ui_src_name pcs-web-ui-%{ui_commit}
|
%global ui_src_name pcs-web-ui-%{ui_commit}
|
||||||
|
|
||||||
%global pcs_snmp_pkg_name pcs-snmp
|
%global pcs_snmp_pkg_name pcs-snmp
|
||||||
@ -40,7 +40,7 @@ ExclusiveArch: i686 x86_64 s390x ppc64le aarch64
|
|||||||
%global version_rubygem_eventmachine 1.2.7
|
%global version_rubygem_eventmachine 1.2.7
|
||||||
%global version_rubygem_ffi 1.15.5
|
%global version_rubygem_ffi 1.15.5
|
||||||
%global version_rubygem_mustermann 3.0.0
|
%global version_rubygem_mustermann 3.0.0
|
||||||
%global version_rubygem_rack 2.2.5
|
%global version_rubygem_rack 2.2.6.4
|
||||||
%global version_rubygem_rack_protection 3.0.5
|
%global version_rubygem_rack_protection 3.0.5
|
||||||
%global version_rubygem_rack_test 2.0.2
|
%global version_rubygem_rack_test 2.0.2
|
||||||
%global version_rubygem_ruby2_keywords 0.0.5
|
%global version_rubygem_ruby2_keywords 0.0.5
|
||||||
@ -115,9 +115,12 @@ Patch6: bz2151164-01-fix-displaying-bool-and-integer-values.patch
|
|||||||
Patch7: bz2159454-01-add-agent-validation-option.patch
|
Patch7: bz2159454-01-add-agent-validation-option.patch
|
||||||
Patch8: bz2158790-01-fix-stonith-watchdog-timeout-validation.patch
|
Patch8: bz2158790-01-fix-stonith-watchdog-timeout-validation.patch
|
||||||
Patch9: bz2166249-01-fix-stonith-watchdog-timeout-offline-update.patch
|
Patch9: bz2166249-01-fix-stonith-watchdog-timeout-offline-update.patch
|
||||||
|
Patch10: bz2180697-01-fix-pcs-config-checkpoint-diff.patch
|
||||||
|
Patch11: bz2180704-01-fix-pcs-stonith-update-scsi.patch
|
||||||
|
|
||||||
# ui patches: >200
|
# ui patches: >200
|
||||||
Patch201: bz2167471-01-fix-broken-typeahead-component.patch
|
Patch201: bz2167471-01-fix-broken-typeahead-component.patch
|
||||||
|
Patch202: bz2183180-01-fix-loading-with-fence-levels.patch
|
||||||
|
|
||||||
# git for patches
|
# git for patches
|
||||||
BuildRequires: git-core
|
BuildRequires: git-core
|
||||||
@ -297,6 +300,7 @@ update_times_patch(){
|
|||||||
%autosetup -D -T -b 100 -a 101 -S git -n %{ui_src_name} -N
|
%autosetup -D -T -b 100 -a 101 -S git -n %{ui_src_name} -N
|
||||||
%autopatch -p1 -m 201
|
%autopatch -p1 -m 201
|
||||||
update_times_patch %{PATCH201}
|
update_times_patch %{PATCH201}
|
||||||
|
update_times_patch %{PATCH202}
|
||||||
|
|
||||||
# patch pcs sources
|
# patch pcs sources
|
||||||
%autosetup -S git -n %{pcs_source_name} -N
|
%autosetup -S git -n %{pcs_source_name} -N
|
||||||
@ -310,6 +314,8 @@ update_times_patch %{PATCH6}
|
|||||||
update_times_patch %{PATCH7}
|
update_times_patch %{PATCH7}
|
||||||
update_times_patch %{PATCH8}
|
update_times_patch %{PATCH8}
|
||||||
update_times_patch %{PATCH9}
|
update_times_patch %{PATCH9}
|
||||||
|
update_times_patch %{PATCH10}
|
||||||
|
update_times_patch %{PATCH11}
|
||||||
|
|
||||||
# prepare dirs/files necessary for building all bundles
|
# prepare dirs/files necessary for building all bundles
|
||||||
# -----------------------------------------------------
|
# -----------------------------------------------------
|
||||||
@ -544,6 +550,14 @@ run_all_tests
|
|||||||
%license pyagentx_LICENSE.txt
|
%license pyagentx_LICENSE.txt
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Tue Mar 28 2023 Michal Pospisil <mpospisi@redhat.com> - 0.11.4-7
|
||||||
|
- Fix displaying differences between configuration checkpoints in “pcs config checkpoint diff” command
|
||||||
|
- Fix “pcs stonith update-scsi-devices” command which was broken since Pacemaker-2.1.5-rc1
|
||||||
|
- Fixed loading of cluster status in the web interface when fencing levels are configured
|
||||||
|
- Fixed a vulnerability in pcs-web-ui-node-modules
|
||||||
|
- Updated bundled rubygem rack
|
||||||
|
- Resolves: rhbz#2179901 rhbz#2180697 rhbz#2180704 rhbz#2180708 rhbz#2180978 rhbz#2183180
|
||||||
|
|
||||||
* Mon Feb 13 2023 Michal Pospisil <mpospisi@redhat.com> - 0.11.4-6
|
* Mon Feb 13 2023 Michal Pospisil <mpospisi@redhat.com> - 0.11.4-6
|
||||||
- Fixed broken filtering in create resource/fence device wizards in the web interface
|
- Fixed broken filtering in create resource/fence device wizards in the web interface
|
||||||
- Added BuildRequires: pam - needed for tier0 tests during build
|
- Added BuildRequires: pam - needed for tier0 tests during build
|
||||||
|
6
sources
6
sources
@ -11,11 +11,11 @@ SHA512 (mustermann-3.0.0.gem) = c33d41281fe2ac80c0af0c5c31dbab2068c73b9da19a4b82
|
|||||||
SHA512 (tilt-2.0.11.gem) = 757a292b05b3ddb2cb8de7680f09433cec85b433e03cd9f738534b99c836fb2129870003a9376c24b6a2f6acb732b51b27968cc0e197a832130d4a35b8dc8239
|
SHA512 (tilt-2.0.11.gem) = 757a292b05b3ddb2cb8de7680f09433cec85b433e03cd9f738534b99c836fb2129870003a9376c24b6a2f6acb732b51b27968cc0e197a832130d4a35b8dc8239
|
||||||
SHA512 (tornado-6.2.0.tar.gz) = b8f98b76198f21bb3a13f44e8698397d7906a415e75621550dfeea8ae3a7e9405f5e314136a93b6714455062501051c145dfd32c71b433715fc8ed591fcb882b
|
SHA512 (tornado-6.2.0.tar.gz) = b8f98b76198f21bb3a13f44e8698397d7906a415e75621550dfeea8ae3a7e9405f5e314136a93b6714455062501051c145dfd32c71b433715fc8ed591fcb882b
|
||||||
SHA512 (rack-test-2.0.2.gem) = 1d395d9504f8d84bcf0e251a9c5873eace29f274b177c7a6bfbdce58f6085ab5170f66d16086e3e159aaa47480d5f993c77b64d613cefda342932c39ad37331d
|
SHA512 (rack-test-2.0.2.gem) = 1d395d9504f8d84bcf0e251a9c5873eace29f274b177c7a6bfbdce58f6085ab5170f66d16086e3e159aaa47480d5f993c77b64d613cefda342932c39ad37331d
|
||||||
SHA512 (pcs-web-ui-node-modules-0.1.16.tar.xz) = 2f94b49bdd7a01a01885cd2d48c5f8bc0338819a90851c404e951ea1aa343ab34f6dc0d4bf40a2cafd6c38bd766062f5577712b25761628e1772fae1e3a4c59f
|
|
||||||
SHA512 (webrick-1.7.0.gem) = 5f242b50300046fe7c22ecd1640a73e5815e05a72bedfebe6bc39c24c92bd61abdd180860de0d194c0eebbc640b507b6892de181d3b577c5372ace0ca6faf2a3
|
SHA512 (webrick-1.7.0.gem) = 5f242b50300046fe7c22ecd1640a73e5815e05a72bedfebe6bc39c24c92bd61abdd180860de0d194c0eebbc640b507b6892de181d3b577c5372ace0ca6faf2a3
|
||||||
SHA512 (pcs-web-ui-0.1.16.tar.gz) = d0451df5fe8d1c3bd14df807f3eeae2e617c7498f52d3db67187553585fa019ba7fe7304e670f430943619f9bdca6b7c0f220d94c3b2423d8e75a1a374cde88c
|
|
||||||
SHA512 (pcs-0.11.4.tar.gz) = df5b7caab7c218676c92db7d8cb24135b3cee1b0aa947851f08659222d7be501e205438e49695339fbad8b14b5637d9cf790e14c9ccc5151e188345924dc0153
|
SHA512 (pcs-0.11.4.tar.gz) = df5b7caab7c218676c92db7d8cb24135b3cee1b0aa947851f08659222d7be501e205438e49695339fbad8b14b5637d9cf790e14c9ccc5151e188345924dc0153
|
||||||
SHA512 (ethon-0.16.0.gem) = 3b31affcee0d5a5be05b5497d4a8d13515f8393f54579a3a9c8de49f78d3f065bb92659434b023f0a8bf8e0cccfbc94b617695b93c4d3f744cccd1eff2e68905
|
SHA512 (ethon-0.16.0.gem) = 3b31affcee0d5a5be05b5497d4a8d13515f8393f54579a3a9c8de49f78d3f065bb92659434b023f0a8bf8e0cccfbc94b617695b93c4d3f744cccd1eff2e68905
|
||||||
SHA512 (rack-2.2.5.gem) = 0e34c8daecd453264fe794c4c16978e8b5b522f41cd134171c79c042ff79d4da59203f69aa5dd62039ef1a62822c069ace4153a82215fed9e4ad8999a8f1d634
|
|
||||||
SHA512 (rack-protection-3.0.5.gem) = 4ed0ee9e8fe08532ff7f2905251af110f3fff0e419da5be50ae3e5a90906e43c39cf8edc219fcfe3e27a72591500c040afcc9552da875773375b170fb91aa9ff
|
SHA512 (rack-protection-3.0.5.gem) = 4ed0ee9e8fe08532ff7f2905251af110f3fff0e419da5be50ae3e5a90906e43c39cf8edc219fcfe3e27a72591500c040afcc9552da875773375b170fb91aa9ff
|
||||||
SHA512 (sinatra-3.0.5.gem) = 047969c56a2a601408a0b27cea9d3e1b7941fdda87ae05ad271be0be07b05f6597433f5fce36325720913bfeb12a3bd1568831a8898d8bff87e5e36d0b8766a6
|
SHA512 (sinatra-3.0.5.gem) = 047969c56a2a601408a0b27cea9d3e1b7941fdda87ae05ad271be0be07b05f6597433f5fce36325720913bfeb12a3bd1568831a8898d8bff87e5e36d0b8766a6
|
||||||
|
SHA512 (rack-2.2.6.4.gem) = dfd2e596e109e3b4ab6edf4679d02cbb863b531476288e194ed7c0d952c7a46a940b3d8b4353a93a3f8aa47f1811517c7375f74ddb6c88dce01b3a58436a8fd2
|
||||||
|
SHA512 (pcs-web-ui-0.1.16.1.tar.gz) = 01427f35276cd5ee2926d6541ec9ccce7e86ec592d294dfb08b086ca701e6b937563ac09ba2b5e82b342b58234f41f6cec38bb22599a3b5181db96e0a0382004
|
||||||
|
SHA512 (pcs-web-ui-node-modules-0.1.16.1.tar.xz) = 6263f14ba017ed98a17985ee2899f25eb97288f62ef2ded90b0217702bc30d0aa80238f1787e34f6ae6b276df2543451eda44422c1df7cade96617209de5c62d
|
||||||
|
Loading…
Reference in New Issue
Block a user