diff --git a/.gitignore b/.gitignore
index c9d343c..b0f1c26 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,10 +7,10 @@ SOURCES/eventmachine-1.2.7.gem
 SOURCES/ffi-1.15.5.gem
 SOURCES/mustermann-3.0.0.gem
 SOURCES/pcs-0.11.4.tar.gz
-SOURCES/pcs-web-ui-0.1.16.tar.gz
-SOURCES/pcs-web-ui-node-modules-0.1.16.tar.xz
+SOURCES/pcs-web-ui-0.1.16.1.tar.gz
+SOURCES/pcs-web-ui-node-modules-0.1.16.1.tar.xz
 SOURCES/pyagentx-0.4.pcs.2.tar.gz
-SOURCES/rack-2.2.5.gem
+SOURCES/rack-2.2.6.4.gem
 SOURCES/rack-protection-3.0.5.gem
 SOURCES/rack-test-2.0.2.gem
 SOURCES/ruby2_keywords-0.0.5.gem
diff --git a/.pcs.metadata b/.pcs.metadata
index 90cd2c4..874f9be 100644
--- a/.pcs.metadata
+++ b/.pcs.metadata
@@ -7,10 +7,10 @@
 97632b7975067266c0b39596de0a4c86d9330658 SOURCES/ffi-1.15.5.gem
 e892678aaf02ccb27f3a6cd58482cda00aea6ce8 SOURCES/mustermann-3.0.0.gem
 b7aecf2f71777395b2b3bb79012de3e658383d4e SOURCES/pcs-0.11.4.tar.gz
-62565f6f573d40a733662f5b9274caa4d275b0de SOURCES/pcs-web-ui-0.1.16.tar.gz
-c079fc5427f91afcefec34c3dc5597eba916effc SOURCES/pcs-web-ui-node-modules-0.1.16.tar.xz
+dba53fa53eb99770f9633fb15f19335fdb2530e2 SOURCES/pcs-web-ui-0.1.16.1.tar.gz
+9a8a94313975247239df63f2842d17f9a526dd3f SOURCES/pcs-web-ui-node-modules-0.1.16.1.tar.xz
 3176b2f2b332c2b6bf79fe882e83feecf3d3f011 SOURCES/pyagentx-0.4.pcs.2.tar.gz
-3ad7b27b68d5dd893ce91f216bb2685ae6c9846a SOURCES/rack-2.2.5.gem
+bbaa023e07bdc4143c5dd18d752c2543f254666f SOURCES/rack-2.2.6.4.gem
 b311f9d60fc3ac0e20078a5aca7c51efa404727c SOURCES/rack-protection-3.0.5.gem
 3c669527ecbcb9f915a83983ec89320c356e1fe3 SOURCES/rack-test-2.0.2.gem
 d017b9e4d1978e0b3ccc3e2a31493809e4693cd3 SOURCES/ruby2_keywords-0.0.5.gem
diff --git a/SOURCES/bz2180697-01-fix-pcs-config-checkpoint-diff.patch b/SOURCES/bz2180697-01-fix-pcs-config-checkpoint-diff.patch
new file mode 100644
index 0000000..9127d8a
--- /dev/null
+++ b/SOURCES/bz2180697-01-fix-pcs-config-checkpoint-diff.patch
@@ -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
+
diff --git a/SOURCES/bz2180704-01-fix-pcs-stonith-update-scsi.patch b/SOURCES/bz2180704-01-fix-pcs-stonith-update-scsi.patch
new file mode 100644
index 0000000..a7fe08b
--- /dev/null
+++ b/SOURCES/bz2180704-01-fix-pcs-stonith-update-scsi.patch
@@ -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
+
diff --git a/SOURCES/bz2183180-01-fix-loading-with-fence-levels.patch b/SOURCES/bz2183180-01-fix-loading-with-fence-levels.patch
new file mode 100644
index 0000000..0b30882
--- /dev/null
+++ b/SOURCES/bz2183180-01-fix-loading-with-fence-levels.patch
@@ -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
+
diff --git a/SPECS/pcs.spec b/SPECS/pcs.spec
index f151372..dafca9f 100644
--- a/SPECS/pcs.spec
+++ b/SPECS/pcs.spec
@@ -1,6 +1,6 @@
 Name: pcs
 Version: 0.11.4
-Release: 6%{?dist}
+Release: 7%{?dist}
 # https://docs.fedoraproject.org/en-US/packaging-guidelines/LicensingGuidelines/
 # https://fedoraproject.org/wiki/Licensing:Main?rd=Licensing#Good_Licenses
 # GPL-2.0-only: pcs
@@ -24,8 +24,8 @@ ExclusiveArch: i686 x86_64 s390x ppc64le aarch64
 %global pcs_source_name %{name}-%{version_or_commit}
 
 # ui_commit can be determined by hash, tag or branch
-%global ui_commit 0.1.16
-%global ui_modules_version 0.1.16
+%global ui_commit 0.1.16.1
+%global ui_modules_version 0.1.16.1
 %global ui_src_name pcs-web-ui-%{ui_commit}
 
 %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_ffi  1.15.5
 %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_test  2.0.2
 %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
 Patch8: bz2158790-01-fix-stonith-watchdog-timeout-validation.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
 Patch201: bz2167471-01-fix-broken-typeahead-component.patch
+Patch202: bz2183180-01-fix-loading-with-fence-levels.patch
 
 # git for patches
 BuildRequires: git-core
@@ -297,6 +300,7 @@ update_times_patch(){
 %autosetup -D -T -b 100 -a 101 -S git -n %{ui_src_name} -N
 %autopatch -p1 -m 201
 update_times_patch %{PATCH201}
+update_times_patch %{PATCH202}
 
 # patch pcs sources
 %autosetup -S git -n %{pcs_source_name} -N
@@ -310,6 +314,8 @@ update_times_patch %{PATCH6}
 update_times_patch %{PATCH7}
 update_times_patch %{PATCH8}
 update_times_patch %{PATCH9}
+update_times_patch %{PATCH10}
+update_times_patch %{PATCH11}
 
 # prepare dirs/files necessary for building all bundles
 # -----------------------------------------------------
@@ -544,6 +550,14 @@ run_all_tests
 %license pyagentx_LICENSE.txt
 
 %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
 - Fixed broken filtering in create resource/fence device wizards in the web interface
 - Added BuildRequires: pam - needed for tier0 tests during build