diff --git a/.gitignore b/.gitignore
index 4d90c03..7adb963 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,25 +1,25 @@
 SOURCES/HAM-logo.png
-SOURCES/backports-3.17.2.gem
+SOURCES/backports-3.23.0.gem
 SOURCES/dacite-1.6.0.tar.gz
-SOURCES/daemons-1.3.1.gem
+SOURCES/daemons-1.4.1.gem
 SOURCES/dataclasses-0.8.tar.gz
-SOURCES/ethon-0.12.0.gem
+SOURCES/ethon-0.15.0.gem
 SOURCES/eventmachine-1.2.7.gem
-SOURCES/ffi-1.13.1.gem
-SOURCES/json-2.3.0.gem
+SOURCES/ffi-1.15.5.gem
+SOURCES/json-2.6.2.gem
 SOURCES/mustermann-1.1.1.gem
 SOURCES/open4-1.3.4-1.gem
-SOURCES/pcs-0.10.12.tar.gz
-SOURCES/pcs-web-ui-0.1.12.tar.gz
-SOURCES/pcs-web-ui-node-modules-0.1.12.tar.xz
+SOURCES/pcs-0.10.14.tar.gz
+SOURCES/pcs-web-ui-0.1.13.tar.gz
+SOURCES/pcs-web-ui-node-modules-0.1.13.tar.xz
 SOURCES/pyagentx-0.4.pcs.2.tar.gz
 SOURCES/python-dateutil-2.8.1.tar.gz
-SOURCES/rack-2.2.3.gem
-SOURCES/rack-protection-2.0.8.1.gem
+SOURCES/rack-2.2.3.1.gem
+SOURCES/rack-protection-2.2.0.gem
 SOURCES/rack-test-1.1.0.gem
 SOURCES/rexml-3.2.5.gem
-SOURCES/ruby2_keywords-0.0.2.gem
-SOURCES/sinatra-2.0.8.1.gem
-SOURCES/thin-1.7.2.gem
+SOURCES/ruby2_keywords-0.0.5.gem
+SOURCES/sinatra-2.2.0.gem
+SOURCES/thin-1.8.1.gem
 SOURCES/tilt-2.0.10.gem
 SOURCES/tornado-6.1.0.tar.gz
diff --git a/.pcs.metadata b/.pcs.metadata
index b16fa68..32792b6 100644
--- a/.pcs.metadata
+++ b/.pcs.metadata
@@ -1,25 +1,25 @@
 679a4ce22a33ffd4d704261a17c00cff98d9499a SOURCES/HAM-logo.png
-28b63a742124da6c9575a1c5e7d7331ef93600b2 SOURCES/backports-3.17.2.gem
+0e11246385a9e0a4bc122b74fb74fe536a234f81 SOURCES/backports-3.23.0.gem
 31546c37fbdc6270d5097687619e9c0db6f1c05c SOURCES/dacite-1.6.0.tar.gz
-e28c1e78d1a6e34e80f4933b494f1e0501939dd3 SOURCES/daemons-1.3.1.gem
+4795a8962cc1608bfec0d91fa4d438c7cfe90c62 SOURCES/daemons-1.4.1.gem
 8b7598273d2ae6dad2b88466aefac55071a41926 SOURCES/dataclasses-0.8.tar.gz
-921ef1be44583a7644ee7f20fe5f26f21d018a04 SOURCES/ethon-0.12.0.gem
+29697a34b8cd9df4a77c650e2a38d0a36cdeee8b SOURCES/ethon-0.15.0.gem
 7a5b2896e210fac9759c786ee4510f265f75b481 SOURCES/eventmachine-1.2.7.gem
-cfa25e7a3760c3ec16723cb8263d9b7a52d0eadf SOURCES/ffi-1.13.1.gem
-0230e8c5a37f1543982e5b04be503dd5f9004b47 SOURCES/json-2.3.0.gem
+97632b7975067266c0b39596de0a4c86d9330658 SOURCES/ffi-1.15.5.gem
+86c10824191e8f351da3fe0a0b6db94a813ada3a SOURCES/json-2.6.2.gem
 50a4e37904485810cb05e27d75c9783e5a8f3402 SOURCES/mustermann-1.1.1.gem
 41a7fe9f8e3e02da5ae76c821b89c5b376a97746 SOURCES/open4-1.3.4-1.gem
-1937b826a36bb8396da227361d13f4c25830929c SOURCES/pcs-0.10.12.tar.gz
-a29bfd22130ac978c5d4a6a82108ce37ad2a5db9 SOURCES/pcs-web-ui-0.1.12.tar.gz
-c9723466d7bfb353899307a5700177f47e7e6cff SOURCES/pcs-web-ui-node-modules-0.1.12.tar.xz
+825eab03553c98465e1de265c151ece149ddba04 SOURCES/pcs-0.10.14.tar.gz
+f7455776936492ce7b241f9801d6bbc946b0461a SOURCES/pcs-web-ui-0.1.13.tar.gz
+bd18d97d611233914828719c97b4d98d079913d2 SOURCES/pcs-web-ui-node-modules-0.1.13.tar.xz
 3176b2f2b332c2b6bf79fe882e83feecf3d3f011 SOURCES/pyagentx-0.4.pcs.2.tar.gz
 bd26127e57f83a10f656b62c46524c15aeb844dd SOURCES/python-dateutil-2.8.1.tar.gz
-345b7169d4d2d62176a225510399963bad62b68f SOURCES/rack-2.2.3.gem
-1f046e23baca8beece3b38c60382f44aa2b2cb41 SOURCES/rack-protection-2.0.8.1.gem
+be609467c819d263c138c417548431b81f8da216 SOURCES/rack-2.2.3.1.gem
+21cfac2453436c6856da31e741bbfa59da4973e1 SOURCES/rack-protection-2.2.0.gem
 b80bc5ca38a885e747271675ba91dd3d02136bf1 SOURCES/rack-test-1.1.0.gem
 e7f48fa5fb2d92e6cb21d6b1638fe41a5a7c4287 SOURCES/rexml-3.2.5.gem
-0be571aacb5d6a212a30af3f322a7000d8af1ef9 SOURCES/ruby2_keywords-0.0.2.gem
-04cca7a5d9d641fe076e4e24dc5b6ff31922f4c3 SOURCES/sinatra-2.0.8.1.gem
-41395e86322ffd31f3a7aef1f697bda3e1e2d6b9 SOURCES/thin-1.7.2.gem
+d017b9e4d1978e0b3ccc3e2a31493809e4693cd3 SOURCES/ruby2_keywords-0.0.5.gem
+5f0d7e63f9d8683f39ad23afe7e00b99602b87cc SOURCES/sinatra-2.2.0.gem
+1ac6292a98e17247b7bb847a35ff868605256f7b SOURCES/thin-1.8.1.gem
 d265c822a6b228392d899e9eb5114613d65e6967 SOURCES/tilt-2.0.10.gem
 c23c617c7a0205e465bebad5b8cdf289ae8402a2 SOURCES/tornado-6.1.0.tar.gz
diff --git a/SOURCES/bz1384485-01-fix-rsc-update-cmd-when-unable-to-get-agent-metadata.patch b/SOURCES/bz1384485-01-fix-rsc-update-cmd-when-unable-to-get-agent-metadata.patch
deleted file mode 100644
index 2960f32..0000000
--- a/SOURCES/bz1384485-01-fix-rsc-update-cmd-when-unable-to-get-agent-metadata.patch
+++ /dev/null
@@ -1,73 +0,0 @@
-From e5fc48f45a60228a82980dcd6d68ca01cf447eac Mon Sep 17 00:00:00 2001
-From: Ondrej Mular <omular@redhat.com>
-Date: Tue, 7 Dec 2021 11:58:09 +0100
-Subject: [PATCH 2/3] fix rsc update cmd when unable to get agent metadata
-
-`resource update` command failed with a traceback when updating a
-resource with a non-existing resource agent
----
- pcs/resource.py                        | 14 ++++++++------
- pcs_test/tier1/legacy/test_resource.py | 21 +++++++++++++++++++++
- 2 files changed, 29 insertions(+), 6 deletions(-)
-
-diff --git a/pcs/resource.py b/pcs/resource.py
-index c0e8b0d9..4514338d 100644
---- a/pcs/resource.py
-+++ b/pcs/resource.py
-@@ -1049,13 +1049,15 @@ def resource_update(lib, args, modifiers, deal_with_guest_change=True):
-         if report_list:
-             process_library_reports(report_list)
-     except lib_ra.ResourceAgentError as e:
--        severity = (
--            reports.ReportItemSeverity.WARNING
--            if modifiers.get("--force")
--            else reports.ReportItemSeverity.ERROR
--        )
-         process_library_reports(
--            [lib_ra.resource_agent_error_to_report_item(e, severity)]
-+            [
-+                lib_ra.resource_agent_error_to_report_item(
-+                    e,
-+                    reports.get_severity(
-+                        reports.codes.FORCE, modifiers.get("--force")
-+                    ),
-+                )
-+            ]
-         )
-     except LibraryError as e:
-         process_library_reports(e.args)
-diff --git a/pcs_test/tier1/legacy/test_resource.py b/pcs_test/tier1/legacy/test_resource.py
-index 3f0e08b9..bae0587a 100644
---- a/pcs_test/tier1/legacy/test_resource.py
-+++ b/pcs_test/tier1/legacy/test_resource.py
-@@ -4879,6 +4879,27 @@ class UpdateInstanceAttrs(
-             ),
-         )
- 
-+    def test_nonexisting_agent(self):
-+        agent = "ocf:pacemaker:nonexistent"
-+        message = (
-+            f"Agent '{agent}' is not installed or does "
-+            "not provide valid metadata: Metadata query for "
-+            f"{agent} failed: Input/output error"
-+        )
-+        self.assert_pcs_success(
-+            f"resource create --force D0 {agent}".split(),
-+            f"Warning: {message}\n",
-+        )
-+
-+        self.assert_pcs_fail(
-+            "resource update D0 test=testA".split(),
-+            f"Error: {message}, use --force to override\n",
-+        )
-+        self.assert_pcs_success(
-+            "resource update --force D0 test=testA".split(),
-+            f"Warning: {message}\n",
-+        )
-+
-     def test_update_existing(self):
-         xml = """
-             <resources>
--- 
-2.31.1
-
diff --git a/SOURCES/bz1786964-01-code-formatting.patch b/SOURCES/bz1786964-01-code-formatting.patch
new file mode 100644
index 0000000..b7419ad
--- /dev/null
+++ b/SOURCES/bz1786964-01-code-formatting.patch
@@ -0,0 +1,2491 @@
+From aa811f21186ac22f25d1c890fb444f8520706803 Mon Sep 17 00:00:00 2001
+From: Tomas Jelinek <tojeline@redhat.com>
+Date: Thu, 14 Jul 2022 16:22:39 +0200
+Subject: [PATCH 1/4] code formatting
+
+---
+ pcs_test/tier0/lib/commands/test_booth.py | 1684 +++++++++------------
+ 1 file changed, 723 insertions(+), 961 deletions(-)
+
+diff --git a/pcs_test/tier0/lib/commands/test_booth.py b/pcs_test/tier0/lib/commands/test_booth.py
+index 6dc0cf3e..2b20a199 100644
+--- a/pcs_test/tier0/lib/commands/test_booth.py
++++ b/pcs_test/tier0/lib/commands/test_booth.py
+@@ -211,22 +211,23 @@ class ConfigSetup(TestCase, FixtureMixin):
+             )
+         )
+         self.env_assist.assert_reports(
+-            [fixture.error(reports.codes.BOOTH_EVEN_PEERS_NUM, number=4)]
++            [
++                fixture.error(reports.codes.BOOTH_EVEN_PEERS_NUM, number=4),
++            ]
+         )
+ 
+     def fixture_config_success(self, instance_name="booth"):
+-        (
+-            self.config.raw_file.write(
+-                file_type_codes.BOOTH_KEY,
+-                self.fixture_key_path(instance_name),
+-                RANDOM_KEY,
+-                name="raw_file.write.key",
+-            ).raw_file.write(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(instance_name),
+-                self.fixture_cfg_content(self.fixture_key_path(instance_name)),
+-                name="raw_file.write.cfg",
+-            )
++        self.config.raw_file.write(
++            file_type_codes.BOOTH_KEY,
++            self.fixture_key_path(instance_name),
++            RANDOM_KEY,
++            name="raw_file.write.key",
++        )
++        self.config.raw_file.write(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(instance_name),
++            self.fixture_cfg_content(self.fixture_key_path(instance_name)),
++            name="raw_file.write.cfg",
+         )
+ 
+     def test_success_default_instance(self):
+@@ -248,19 +249,18 @@ class ConfigSetup(TestCase, FixtureMixin):
+         )
+ 
+     def test_files_exist_config(self):
+-        (
+-            self.config.raw_file.write(
+-                file_type_codes.BOOTH_KEY,
+-                self.fixture_key_path(),
+-                RANDOM_KEY,
+-                name="raw_file.write.key",
+-            ).raw_file.write(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                self.fixture_cfg_content(),
+-                already_exists=True,
+-                name="raw_file.write.cfg",
+-            )
++        self.config.raw_file.write(
++            file_type_codes.BOOTH_KEY,
++            self.fixture_key_path(),
++            RANDOM_KEY,
++            name="raw_file.write.key",
++        )
++        self.config.raw_file.write(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            self.fixture_cfg_content(),
++            already_exists=True,
++            name="raw_file.write.cfg",
+         )
+ 
+         self.env_assist.assert_raise_library_error(
+@@ -283,14 +283,12 @@ class ConfigSetup(TestCase, FixtureMixin):
+         )
+ 
+     def test_files_exist_key(self):
+-        (
+-            self.config.raw_file.write(
+-                file_type_codes.BOOTH_KEY,
+-                self.fixture_key_path(),
+-                RANDOM_KEY,
+-                already_exists=True,
+-                name="raw_file.write.key",
+-            )
++        self.config.raw_file.write(
++            file_type_codes.BOOTH_KEY,
++            self.fixture_key_path(),
++            RANDOM_KEY,
++            already_exists=True,
++            name="raw_file.write.key",
+         )
+ 
+         self.env_assist.assert_raise_library_error(
+@@ -313,20 +311,19 @@ class ConfigSetup(TestCase, FixtureMixin):
+         )
+ 
+     def test_files_exist_forced(self):
+-        (
+-            self.config.raw_file.write(
+-                file_type_codes.BOOTH_KEY,
+-                self.fixture_key_path(),
+-                RANDOM_KEY,
+-                can_overwrite=True,
+-                name="raw_file.write.key",
+-            ).raw_file.write(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                self.fixture_cfg_content(),
+-                can_overwrite=True,
+-                name="raw_file.write.cfg",
+-            )
++        self.config.raw_file.write(
++            file_type_codes.BOOTH_KEY,
++            self.fixture_key_path(),
++            RANDOM_KEY,
++            can_overwrite=True,
++            name="raw_file.write.key",
++        )
++        self.config.raw_file.write(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            self.fixture_cfg_content(),
++            can_overwrite=True,
++            name="raw_file.write.cfg",
+         )
+ 
+         commands.config_setup(
+@@ -337,7 +334,6 @@ class ConfigSetup(TestCase, FixtureMixin):
+         )
+ 
+     def _assert_write_config_error(self, error, booth_dir_exists):
+-
+         self.config.raw_file.write(
+             file_type_codes.BOOTH_KEY,
+             self.fixture_key_path(),
+@@ -444,11 +440,7 @@ class ConfigSetup(TestCase, FixtureMixin):
+         )
+         env = self.env_assist.get_env()
+ 
+-        commands.config_setup(
+-            env,
+-            self.sites,
+-            self.arbitrators,
+-        )
++        commands.config_setup(env, self.sites, self.arbitrators)
+ 
+         self.assertEqual(
+             env.get_booth_env(name="").export(),
+@@ -491,38 +483,34 @@ class ConfigDestroy(TestCase, FixtureMixin):
+         self.env_assist, self.config = get_env_tools(self)
+ 
+     def fixture_config_booth_not_used(self, instance_name="booth"):
+-        (
+-            self.config.runner.cib.load()
+-            .services.is_running(
+-                "booth", instance=instance_name, return_value=False
+-            )
+-            .services.is_enabled(
+-                "booth", instance=instance_name, return_value=False
+-            )
++        self.config.runner.cib.load()
++        self.config.services.is_running(
++            "booth", instance=instance_name, return_value=False
++        )
++        self.config.services.is_enabled(
++            "booth", instance=instance_name, return_value=False
+         )
+ 
+     def fixture_config_success(self, instance_name="booth"):
+         self.fixture_config_booth_not_used(instance_name)
+-        (
+-            self.config.raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(instance_name),
+-                content=self.fixture_cfg_content(
+-                    self.fixture_key_path(instance_name)
+-                ),
+-            )
+-            .raw_file.remove(
+-                file_type_codes.BOOTH_KEY,
+-                self.fixture_key_path(instance_name),
+-                fail_if_file_not_found=False,
+-                name="raw_file.remove.key",
+-            )
+-            .raw_file.remove(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(instance_name),
+-                fail_if_file_not_found=True,
+-                name="raw_file.remove.cfg",
+-            )
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(instance_name),
++            content=self.fixture_cfg_content(
++                self.fixture_key_path(instance_name)
++            ),
++        )
++        self.config.raw_file.remove(
++            file_type_codes.BOOTH_KEY,
++            self.fixture_key_path(instance_name),
++            fail_if_file_not_found=False,
++            name="raw_file.remove.key",
++        )
++        self.config.raw_file.remove(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(instance_name),
++            fail_if_file_not_found=True,
++            name="raw_file.remove.cfg",
+         )
+ 
+     def test_invalid_instance(self):
+@@ -540,9 +528,7 @@ class ConfigDestroy(TestCase, FixtureMixin):
+ 
+     def test_success_default_instance(self):
+         self.fixture_config_success()
+-        commands.config_destroy(
+-            self.env_assist.get_env(),
+-        )
++        commands.config_destroy(self.env_assist.get_env())
+ 
+     def test_success_custom_instance(self):
+         instance_name = "my_booth"
+@@ -553,23 +539,20 @@ class ConfigDestroy(TestCase, FixtureMixin):
+ 
+     def test_success_no_booth_key(self):
+         self.fixture_config_booth_not_used()
+-        (
+-            self.config.raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                content=bytes(),
+-            ).raw_file.remove(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                fail_if_file_not_found=True,
+-                name="raw_file.remove.cfg",
+-            )
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            content=bytes(),
+         )
+-
+-        commands.config_destroy(
+-            self.env_assist.get_env(),
++        self.config.raw_file.remove(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            fail_if_file_not_found=True,
++            name="raw_file.remove.cfg",
+         )
+ 
++        commands.config_destroy(self.env_assist.get_env())
++
+     def test_not_live_booth(self):
+         self.config.env.set_booth(
+             {
+@@ -603,9 +586,7 @@ class ConfigDestroy(TestCase, FixtureMixin):
+             [
+                 fixture.error(
+                     reports.codes.LIVE_ENVIRONMENT_REQUIRED,
+-                    forbidden_options=[
+-                        file_type_codes.CIB,
+-                    ],
++                    forbidden_options=[file_type_codes.CIB],
+                 ),
+             ],
+             expected_in_processor=False,
+@@ -640,20 +621,16 @@ class ConfigDestroy(TestCase, FixtureMixin):
+     def test_booth_config_in_use(self):
+         instance_name = "booth"
+ 
+-        (
+-            self.config.runner.cib.load(resources=self.fixture_cib_resources())
+-            .services.is_running(
+-                "booth", instance=instance_name, return_value=True
+-            )
+-            .services.is_enabled(
+-                "booth", instance=instance_name, return_value=True
+-            )
++        self.config.runner.cib.load(resources=self.fixture_cib_resources())
++        self.config.services.is_running(
++            "booth", instance=instance_name, return_value=True
++        )
++        self.config.services.is_enabled(
++            "booth", instance=instance_name, return_value=True
+         )
+ 
+         self.env_assist.assert_raise_library_error(
+-            lambda: commands.config_destroy(
+-                self.env_assist.get_env(),
+-            ),
++            lambda: commands.config_destroy(self.env_assist.get_env()),
+         )
+ 
+         self.env_assist.assert_reports(
+@@ -682,18 +659,14 @@ class ConfigDestroy(TestCase, FixtureMixin):
+     def test_cannot_read_config(self):
+         error = "an error"
+         self.fixture_config_booth_not_used()
+-        (
+-            self.config.raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                exception_msg=error,
+-            )
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            exception_msg=error,
+         )
+ 
+         self.env_assist.assert_raise_library_error(
+-            lambda: commands.config_destroy(
+-                self.env_assist.get_env(),
+-            ),
++            lambda: commands.config_destroy(self.env_assist.get_env()),
+         )
+         self.env_assist.assert_reports(
+             [
+@@ -711,17 +684,16 @@ class ConfigDestroy(TestCase, FixtureMixin):
+     def test_cannot_read_config_forced(self):
+         error = "an error"
+         self.fixture_config_booth_not_used()
+-        (
+-            self.config.raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                exception_msg=error,
+-            ).raw_file.remove(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                fail_if_file_not_found=True,
+-                name="raw_file.remove.cfg",
+-            )
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            exception_msg=error,
++        )
++        self.config.raw_file.remove(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            fail_if_file_not_found=True,
++            name="raw_file.remove.cfg",
+         )
+ 
+         commands.config_destroy(
+@@ -742,18 +714,14 @@ class ConfigDestroy(TestCase, FixtureMixin):
+ 
+     def test_config_parse_error(self):
+         self.fixture_config_booth_not_used()
+-        (
+-            self.config.raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                content="invalid config".encode("utf-8"),
+-            )
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            content="invalid config".encode("utf-8"),
+         )
+ 
+         self.env_assist.assert_raise_library_error(
+-            lambda: commands.config_destroy(
+-                self.env_assist.get_env(),
+-            ),
++            lambda: commands.config_destroy(self.env_assist.get_env()),
+         )
+         self.env_assist.assert_reports(
+             [
+@@ -768,17 +736,16 @@ class ConfigDestroy(TestCase, FixtureMixin):
+ 
+     def test_config_parse_error_forced(self):
+         self.fixture_config_booth_not_used()
+-        (
+-            self.config.raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                content="invalid config".encode("utf-8"),
+-            ).raw_file.remove(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                fail_if_file_not_found=True,
+-                name="raw_file.remove.cfg",
+-            )
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            content="invalid config".encode("utf-8"),
++        )
++        self.config.raw_file.remove(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            fail_if_file_not_found=True,
++            name="raw_file.remove.cfg",
+         )
+ 
+         commands.config_destroy(
+@@ -797,52 +764,45 @@ class ConfigDestroy(TestCase, FixtureMixin):
+ 
+     def test_key_already_deleted(self):
+         self.fixture_config_booth_not_used()
+-        (
+-            self.config.raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                content=self.fixture_cfg_content(),
+-            )
+-            .raw_file.remove(
+-                file_type_codes.BOOTH_KEY,
+-                self.fixture_key_path(),
+-                fail_if_file_not_found=False,
+-                file_not_found_exception=True,
+-                name="raw_file.remove.key",
+-            )
+-            .raw_file.remove(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                fail_if_file_not_found=True,
+-                name="raw_file.remove.cfg",
+-            )
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            content=self.fixture_cfg_content(),
+         )
+-
+-        commands.config_destroy(
+-            self.env_assist.get_env(),
++        self.config.raw_file.remove(
++            file_type_codes.BOOTH_KEY,
++            self.fixture_key_path(),
++            fail_if_file_not_found=False,
++            file_not_found_exception=True,
++            name="raw_file.remove.key",
++        )
++        self.config.raw_file.remove(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            fail_if_file_not_found=True,
++            name="raw_file.remove.cfg",
+         )
+ 
++        commands.config_destroy(self.env_assist.get_env())
++
+     def test_cannot_delete_key(self):
+         error = "an error"
+         self.fixture_config_booth_not_used()
+-        (
+-            self.config.raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                content=self.fixture_cfg_content(),
+-            ).raw_file.remove(
+-                file_type_codes.BOOTH_KEY,
+-                self.fixture_key_path(),
+-                fail_if_file_not_found=False,
+-                exception_msg=error,
+-                name="raw_file.remove.key",
+-            )
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            content=self.fixture_cfg_content(),
++        )
++        self.config.raw_file.remove(
++            file_type_codes.BOOTH_KEY,
++            self.fixture_key_path(),
++            fail_if_file_not_found=False,
++            exception_msg=error,
++            name="raw_file.remove.key",
+         )
+ 
+         self.env_assist.assert_raise_library_error(
+-            lambda: commands.config_destroy(
+-                self.env_assist.get_env(),
+-            ),
++            lambda: commands.config_destroy(self.env_assist.get_env()),
+         )
+         self.env_assist.assert_reports(
+             [
+@@ -860,25 +820,23 @@ class ConfigDestroy(TestCase, FixtureMixin):
+     def test_cannot_delete_key_forced(self):
+         error = "an error"
+         self.fixture_config_booth_not_used()
+-        (
+-            self.config.raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                content=self.fixture_cfg_content(),
+-            )
+-            .raw_file.remove(
+-                file_type_codes.BOOTH_KEY,
+-                self.fixture_key_path(),
+-                fail_if_file_not_found=False,
+-                exception_msg=error,
+-                name="raw_file.remove.key",
+-            )
+-            .raw_file.remove(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                fail_if_file_not_found=True,
+-                name="raw_file.remove.cfg",
+-            )
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            content=self.fixture_cfg_content(),
++        )
++        self.config.raw_file.remove(
++            file_type_codes.BOOTH_KEY,
++            self.fixture_key_path(),
++            fail_if_file_not_found=False,
++            exception_msg=error,
++            name="raw_file.remove.key",
++        )
++        self.config.raw_file.remove(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            fail_if_file_not_found=True,
++            name="raw_file.remove.cfg",
+         )
+ 
+         commands.config_destroy(
+@@ -900,31 +858,27 @@ class ConfigDestroy(TestCase, FixtureMixin):
+     def test_cannot_delete_config_forced(self):
+         error = "an error"
+         self.fixture_config_booth_not_used()
+-        (
+-            self.config.raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                content=self.fixture_cfg_content(),
+-            )
+-            .raw_file.remove(
+-                file_type_codes.BOOTH_KEY,
+-                self.fixture_key_path(),
+-                fail_if_file_not_found=False,
+-                name="raw_file.remove.key",
+-            )
+-            .raw_file.remove(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                fail_if_file_not_found=True,
+-                exception_msg=error,
+-                name="raw_file.remove.cfg",
+-            )
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            content=self.fixture_cfg_content(),
++        )
++        self.config.raw_file.remove(
++            file_type_codes.BOOTH_KEY,
++            self.fixture_key_path(),
++            fail_if_file_not_found=False,
++            name="raw_file.remove.key",
++        )
++        self.config.raw_file.remove(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            fail_if_file_not_found=True,
++            exception_msg=error,
++            name="raw_file.remove.cfg",
+         )
+ 
+         self.env_assist.assert_raise_library_error(
+-            lambda: commands.config_destroy(
+-                self.env_assist.get_env(),
+-            ),
++            lambda: commands.config_destroy(self.env_assist.get_env()),
+         )
+         self.env_assist.assert_reports(
+             [
+@@ -941,22 +895,19 @@ class ConfigDestroy(TestCase, FixtureMixin):
+     def test_keyfile_outside_of_booth_dir(self):
+         key_path = "/tmp/pcs_test/booth.key"
+         self.fixture_config_booth_not_used()
+-        (
+-            self.config.raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                content=f"authfile = {key_path}".encode("utf-8"),
+-            ).raw_file.remove(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                fail_if_file_not_found=True,
+-                name="raw_file.remove.cfg",
+-            )
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            content=f"authfile = {key_path}".encode("utf-8"),
+         )
+-
+-        commands.config_destroy(
+-            self.env_assist.get_env(),
++        self.config.raw_file.remove(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            fail_if_file_not_found=True,
++            name="raw_file.remove.cfg",
+         )
++
++        commands.config_destroy(self.env_assist.get_env())
+         self.env_assist.assert_reports(
+             [
+                 fixture.warn(
+@@ -987,29 +938,23 @@ class ConfigText(TestCase, FixtureMixin):
+ 
+     def test_success_default_instance(self):
+         config_content = "my config content".encode("utf-8")
+-        (
+-            self.config.raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                content=config_content,
+-            )
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            content=config_content,
+         )
+         self.assertEqual(
+-            commands.config_text(
+-                self.env_assist.get_env(),
+-            ),
++            commands.config_text(self.env_assist.get_env()),
+             config_content,
+         )
+ 
+     def test_success_custom_instance(self):
+         instance_name = "my_booth"
+         config_content = "my config content".encode("utf-8")
+-        (
+-            self.config.raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(instance_name),
+-                content=config_content,
+-            )
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(instance_name),
++            content=config_content,
+         )
+         self.assertEqual(
+             commands.config_text(
+@@ -1029,9 +974,7 @@ class ConfigText(TestCase, FixtureMixin):
+             }
+         )
+         self.env_assist.assert_raise_library_error(
+-            lambda: commands.config_text(
+-                self.env_assist.get_env(),
+-            ),
++            lambda: commands.config_text(self.env_assist.get_env()),
+             [
+                 fixture.error(
+                     reports.codes.LIVE_ENVIRONMENT_REQUIRED,
+@@ -1046,17 +989,13 @@ class ConfigText(TestCase, FixtureMixin):
+ 
+     def test_cannot_read_config(self):
+         error = "an error"
+-        (
+-            self.config.raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                exception_msg=error,
+-            )
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            exception_msg=error,
+         )
+         self.env_assist.assert_raise_library_error(
+-            lambda: commands.config_text(
+-                self.env_assist.get_env(),
+-            ),
++            lambda: commands.config_text(self.env_assist.get_env()),
+         )
+         self.env_assist.assert_reports(
+             [
+@@ -1073,12 +1012,10 @@ class ConfigText(TestCase, FixtureMixin):
+     def test_remote_success(self):
+         instance_name = "my_booth"
+         config_content = "my config content"
+-        (
+-            self.config.http.booth.get_config(
+-                instance_name,
+-                config_data=config_content,
+-                node_labels=["node1"],
+-            )
++        self.config.http.booth.get_config(
++            instance_name,
++            config_data=config_content,
++            node_labels=["node1"],
+         )
+         self.assertEqual(
+             commands.config_text(
+@@ -1095,22 +1032,19 @@ class ConfigText(TestCase, FixtureMixin):
+         server_error = (
+             "some error like 'config does not exist' or 'instance name invalid'"
+         )
+-        (
+-            self.config.http.booth.get_config(
+-                instance_name,
+-                communication_list=[
+-                    dict(
+-                        label=node_name,
+-                        response_code=400,
+-                        output=server_error,
+-                    )
+-                ],
+-            )
++        self.config.http.booth.get_config(
++            instance_name,
++            communication_list=[
++                dict(
++                    label=node_name,
++                    response_code=400,
++                    output=server_error,
++                )
++            ],
+         )
+         self.env_assist.assert_raise_library_error(
+             lambda: commands.config_text(
+-                self.env_assist.get_env(),
+-                node_name=node_name,
++                self.env_assist.get_env(), node_name=node_name
+             ),
+         )
+         self.env_assist.assert_reports(
+@@ -1127,21 +1061,18 @@ class ConfigText(TestCase, FixtureMixin):
+     def test_remote_bad_response(self):
+         instance_name = "booth"
+         node_name = "node1"
+-        (
+-            self.config.http.booth.get_config(
+-                instance_name,
+-                communication_list=[
+-                    dict(
+-                        label=node_name,
+-                        output="not a json",
+-                    )
+-                ],
+-            )
++        self.config.http.booth.get_config(
++            instance_name,
++            communication_list=[
++                dict(
++                    label=node_name,
++                    output="not a json",
++                )
++            ],
+         )
+         self.env_assist.assert_raise_library_error(
+             lambda: commands.config_text(
+-                self.env_assist.get_env(),
+-                node_name=node_name,
++                self.env_assist.get_env(), node_name=node_name
+             ),
+         )
+         self.env_assist.assert_reports(
+@@ -1157,23 +1088,20 @@ class ConfigText(TestCase, FixtureMixin):
+         instance_name = "booth"
+         node_name = "node1"
+         error = "an error"
+-        (
+-            self.config.http.booth.get_config(
+-                instance_name,
+-                communication_list=[
+-                    dict(
+-                        label=node_name,
+-                        was_connected=False,
+-                        errno=1,
+-                        error_msg=error,
+-                    )
+-                ],
+-            )
++        self.config.http.booth.get_config(
++            instance_name,
++            communication_list=[
++                dict(
++                    label=node_name,
++                    was_connected=False,
++                    errno=1,
++                    error_msg=error,
++                )
++            ],
+         )
+         self.env_assist.assert_raise_library_error(
+             lambda: commands.config_text(
+-                self.env_assist.get_env(),
+-                node_name=node_name,
++                self.env_assist.get_env(), node_name=node_name
+             ),
+         )
+         self.env_assist.assert_reports(
+@@ -1208,22 +1136,21 @@ class ConfigTicketAdd(TestCase, FixtureMixin):
+         )
+ 
+     def fixture_config_success(self, instance_name="booth"):
+-        (
+-            self.config.raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(instance_name),
+-                content=self.fixture_cfg_content(
+-                    self.fixture_key_path(instance_name)
+-                ),
+-            ).raw_file.write(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(instance_name),
+-                self.fixture_cfg_content(
+-                    self.fixture_key_path(instance_name),
+-                    ticket_list=[["ticketA", []]],
+-                ),
+-                can_overwrite=True,
+-            )
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(instance_name),
++            content=self.fixture_cfg_content(
++                self.fixture_key_path(instance_name)
++            ),
++        )
++        self.config.raw_file.write(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(instance_name),
++            self.fixture_cfg_content(
++                self.fixture_key_path(instance_name),
++                ticket_list=[["ticketA", []]],
++            ),
++            can_overwrite=True,
+         )
+ 
+     def test_success_default_instance(self):
+@@ -1268,21 +1195,20 @@ class ConfigTicketAdd(TestCase, FixtureMixin):
+         )
+ 
+     def test_success_ticket_options(self):
+-        (
+-            self.config.raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                content=self.fixture_cfg_content(),
+-            ).raw_file.write(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                self.fixture_cfg_content(
+-                    ticket_list=[
+-                        ["ticketA", [("retries", "10"), ("timeout", "20")]]
+-                    ]
+-                ),
+-                can_overwrite=True,
+-            )
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            content=self.fixture_cfg_content(),
++        )
++        self.config.raw_file.write(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            self.fixture_cfg_content(
++                ticket_list=[
++                    ["ticketA", [("retries", "10"), ("timeout", "20")]]
++                ]
++            ),
++            can_overwrite=True,
+         )
+         commands.config_ticket_add(
+             self.env_assist.get_env(),
+@@ -1291,12 +1217,10 @@ class ConfigTicketAdd(TestCase, FixtureMixin):
+         )
+ 
+     def test_ticket_already_exists(self):
+-        (
+-            self.config.raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                content=self.fixture_cfg_content(ticket_list=[["ticketA", []]]),
+-            )
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            content=self.fixture_cfg_content(ticket_list=[["ticketA", []]]),
+         )
+         self.env_assist.assert_raise_library_error(
+             lambda: commands.config_ticket_add(
+@@ -1316,12 +1240,10 @@ class ConfigTicketAdd(TestCase, FixtureMixin):
+         )
+ 
+     def test_validator_errors(self):
+-        (
+-            self.config.raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                content=self.fixture_cfg_content(),
+-            )
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            content=self.fixture_cfg_content(),
+         )
+         self.env_assist.assert_raise_library_error(
+             lambda: commands.config_ticket_add(
+@@ -1356,19 +1278,16 @@ class ConfigTicketAdd(TestCase, FixtureMixin):
+         )
+ 
+     def test_invalid_ticket_options_forced(self):
+-        (
+-            self.config.raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                content=self.fixture_cfg_content(),
+-            ).raw_file.write(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                self.fixture_cfg_content(
+-                    ticket_list=[["ticketA", [("a", "A")]]]
+-                ),
+-                can_overwrite=True,
+-            )
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            content=self.fixture_cfg_content(),
++        )
++        self.config.raw_file.write(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            self.fixture_cfg_content(ticket_list=[["ticketA", [("a", "A")]]]),
++            can_overwrite=True,
+         )
+         commands.config_ticket_add(
+             self.env_assist.get_env(),
+@@ -1399,12 +1318,10 @@ class ConfigTicketAdd(TestCase, FixtureMixin):
+         )
+ 
+     def test_config_parse_error(self):
+-        (
+-            self.config.raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                content="invalid config".encode("utf-8"),
+-            )
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            content="invalid config".encode("utf-8"),
+         )
+         self.env_assist.assert_raise_library_error(
+             lambda: commands.config_ticket_add(
+@@ -1425,12 +1342,10 @@ class ConfigTicketAdd(TestCase, FixtureMixin):
+ 
+     def test_cannot_read_config(self):
+         error = "an error"
+-        (
+-            self.config.raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                exception_msg=error,
+-            )
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            exception_msg=error,
+         )
+         self.env_assist.assert_raise_library_error(
+             lambda: commands.config_ticket_add(
+@@ -1480,18 +1395,17 @@ class ConfigTicketAdd(TestCase, FixtureMixin):
+ 
+     def test_cannot_write_config(self):
+         error = "an error"
+-        (
+-            self.config.raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                content=self.fixture_cfg_content(),
+-            ).raw_file.write(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                self.fixture_cfg_content(ticket_list=[["ticketA", []]]),
+-                can_overwrite=True,
+-                exception_msg=error,
+-            )
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            content=self.fixture_cfg_content(),
++        )
++        self.config.raw_file.write(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            self.fixture_cfg_content(ticket_list=[["ticketA", []]]),
++            can_overwrite=True,
++            exception_msg=error,
+         )
+         self.env_assist.assert_raise_library_error(
+             lambda: commands.config_ticket_add(
+@@ -1532,46 +1446,40 @@ class ConfigTicketRemove(TestCase, FixtureMixin):
+         )
+ 
+     def fixture_config_success(self, instance_name="booth"):
+-        (
+-            self.config.raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(instance_name),
+-                self.fixture_cfg_content(
+-                    self.fixture_key_path(instance_name),
+-                    ticket_list=[
+-                        ["ticketA", []],
+-                        ["ticketB", []],
+-                        ["ticketC", []],
+-                    ],
+-                ),
+-            ).raw_file.write(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(instance_name),
+-                self.fixture_cfg_content(
+-                    self.fixture_key_path(instance_name),
+-                    ticket_list=[
+-                        ["ticketA", []],
+-                        ["ticketC", []],
+-                    ],
+-                ),
+-                can_overwrite=True,
+-            )
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(instance_name),
++            self.fixture_cfg_content(
++                self.fixture_key_path(instance_name),
++                ticket_list=[
++                    ["ticketA", []],
++                    ["ticketB", []],
++                    ["ticketC", []],
++                ],
++            ),
++        )
++        self.config.raw_file.write(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(instance_name),
++            self.fixture_cfg_content(
++                self.fixture_key_path(instance_name),
++                ticket_list=[
++                    ["ticketA", []],
++                    ["ticketC", []],
++                ],
++            ),
++            can_overwrite=True,
+         )
+ 
+     def test_success_default_instance(self):
+         self.fixture_config_success()
+-        commands.config_ticket_remove(
+-            self.env_assist.get_env(),
+-            "ticketB",
+-        )
++        commands.config_ticket_remove(self.env_assist.get_env(), "ticketB")
+ 
+     def test_success_custom_instance(self):
+         instance_name = "my_booth"
+         self.fixture_config_success(instance_name=instance_name)
+         commands.config_ticket_remove(
+-            self.env_assist.get_env(),
+-            "ticketB",
+-            instance_name=instance_name,
++            self.env_assist.get_env(), "ticketB", instance_name=instance_name
+         )
+ 
+     def test_success_not_live(self):
+@@ -1602,51 +1510,44 @@ class ConfigTicketRemove(TestCase, FixtureMixin):
+         )
+ 
+     def test_success_ticket_options(self):
+-        (
+-            self.config.raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                self.fixture_cfg_content(
+-                    ticket_list=[
+-                        ["ticketA", [("a1", "A1"), ("a2", "A2")]],
+-                        ["ticketB", [("b1", "B1"), ("b2", "B2")]],
+-                        ["ticketC", [("c1", "C1"), ("c2", "C2")]],
+-                    ]
+-                ),
+-            ).raw_file.write(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                self.fixture_cfg_content(
+-                    ticket_list=[
+-                        ["ticketA", [("a1", "A1"), ("a2", "A2")]],
+-                        ["ticketC", [("c1", "C1"), ("c2", "C2")]],
+-                    ]
+-                ),
+-                can_overwrite=True,
+-            )
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            self.fixture_cfg_content(
++                ticket_list=[
++                    ["ticketA", [("a1", "A1"), ("a2", "A2")]],
++                    ["ticketB", [("b1", "B1"), ("b2", "B2")]],
++                    ["ticketC", [("c1", "C1"), ("c2", "C2")]],
++                ]
++            ),
+         )
+-        commands.config_ticket_remove(
+-            self.env_assist.get_env(),
+-            "ticketB",
++        self.config.raw_file.write(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            self.fixture_cfg_content(
++                ticket_list=[
++                    ["ticketA", [("a1", "A1"), ("a2", "A2")]],
++                    ["ticketC", [("c1", "C1"), ("c2", "C2")]],
++                ]
++            ),
++            can_overwrite=True,
+         )
++        commands.config_ticket_remove(self.env_assist.get_env(), "ticketB")
+ 
+     def test_ticket_does_not_exist(self):
+-        (
+-            self.config.raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                self.fixture_cfg_content(
+-                    ticket_list=[
+-                        ["ticketA", []],
+-                        ["ticketC", []],
+-                    ]
+-                ),
+-            )
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            self.fixture_cfg_content(
++                ticket_list=[
++                    ["ticketA", []],
++                    ["ticketC", []],
++                ]
++            ),
+         )
+         self.env_assist.assert_raise_library_error(
+             lambda: commands.config_ticket_remove(
+-                self.env_assist.get_env(),
+-                "ticketB",
++                self.env_assist.get_env(), "ticketB"
+             )
+         )
+         self.env_assist.assert_reports(
+@@ -1659,17 +1560,14 @@ class ConfigTicketRemove(TestCase, FixtureMixin):
+         )
+ 
+     def test_config_parse_error(self):
+-        (
+-            self.config.raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                content="invalid config".encode("utf-8"),
+-            )
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            content="invalid config".encode("utf-8"),
+         )
+         self.env_assist.assert_raise_library_error(
+             lambda: commands.config_ticket_remove(
+-                self.env_assist.get_env(),
+-                "ticketB",
++                self.env_assist.get_env(), "ticketB"
+             )
+         )
+         self.env_assist.assert_reports(
+@@ -1684,17 +1582,14 @@ class ConfigTicketRemove(TestCase, FixtureMixin):
+ 
+     def test_cannot_read_config(self):
+         error = "an error"
+-        (
+-            self.config.raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                exception_msg=error,
+-            )
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            exception_msg=error,
+         )
+         self.env_assist.assert_raise_library_error(
+             lambda: commands.config_ticket_remove(
+-                self.env_assist.get_env(),
+-                "ticketB",
++                self.env_assist.get_env(), "ticketB"
+             )
+         )
+         self.env_assist.assert_reports(
+@@ -1719,8 +1614,7 @@ class ConfigTicketRemove(TestCase, FixtureMixin):
+         )
+         self.env_assist.assert_raise_library_error(
+             lambda: commands.config_ticket_remove(
+-                self.env_assist.get_env(),
+-                "ticketB",
++                self.env_assist.get_env(), "ticketB"
+             )
+         )
+         self.env_assist.assert_reports(
+@@ -1737,27 +1631,25 @@ class ConfigTicketRemove(TestCase, FixtureMixin):
+ 
+     def test_cannot_write_config(self):
+         error = "an error"
+-        (
+-            self.config.raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                self.fixture_cfg_content(
+-                    ticket_list=[
+-                        ["ticketB", []],
+-                    ]
+-                ),
+-            ).raw_file.write(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                self.fixture_cfg_content(),
+-                can_overwrite=True,
+-                exception_msg=error,
+-            )
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            self.fixture_cfg_content(
++                ticket_list=[
++                    ["ticketB", []],
++                ]
++            ),
++        )
++        self.config.raw_file.write(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            self.fixture_cfg_content(),
++            can_overwrite=True,
++            exception_msg=error,
+         )
+         self.env_assist.assert_raise_library_error(
+             lambda: commands.config_ticket_remove(
+-                self.env_assist.get_env(),
+-                "ticketB",
++                self.env_assist.get_env(), "ticketB"
+             )
+         )
+         self.env_assist.assert_reports(
+@@ -1792,40 +1684,35 @@ class CreateInCluster(TestCase, FixtureMixin):
+         )
+ 
+     def fixture_config_success(self, instance_name="booth"):
+-        (
+-            self.config.runner.cib.load()
+-            .raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(instance_name),
+-                content=self.fixture_cfg_content(
+-                    self.fixture_key_path(instance_name)
+-                ),
+-            )
+-            .runner.pcmk.load_agent(
+-                agent_name="ocf:heartbeat:IPaddr2",
+-                name="runner.pcmk.load_agent.ipaddr2",
+-            )
+-            .runner.pcmk.load_agent(
+-                agent_name="ocf:pacemaker:booth-site",
+-                name="runner.pcmk.load_agent.booth-site",
+-            )
+-            .env.push_cib(resources=self.fixture_cib_booth_group(instance_name))
++        self.config.runner.cib.load()
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(instance_name),
++            content=self.fixture_cfg_content(
++                self.fixture_key_path(instance_name)
++            ),
++        )
++        self.config.runner.pcmk.load_agent(
++            agent_name="ocf:heartbeat:IPaddr2",
++            name="runner.pcmk.load_agent.ipaddr2",
++        )
++        self.config.runner.pcmk.load_agent(
++            agent_name="ocf:pacemaker:booth-site",
++            name="runner.pcmk.load_agent.booth-site",
++        )
++        self.config.env.push_cib(
++            resources=self.fixture_cib_booth_group(instance_name)
+         )
+ 
+     def test_success_default_instance(self):
+         self.fixture_config_success()
+-        commands.create_in_cluster(
+-            self.env_assist.get_env(),
+-            self.site_ip,
+-        )
++        commands.create_in_cluster(self.env_assist.get_env(), self.site_ip)
+ 
+     def test_success_custom_instance(self):
+         instance_name = "my_booth"
+         self.fixture_config_success(instance_name=instance_name)
+         commands.create_in_cluster(
+-            self.env_assist.get_env(),
+-            self.site_ip,
+-            instance_name=instance_name,
++            self.env_assist.get_env(), self.site_ip, instance_name=instance_name
+         )
+ 
+     def test_success_not_live_cib(self):
+@@ -1833,29 +1720,24 @@ class CreateInCluster(TestCase, FixtureMixin):
+         env = dict(CIB_file=tmp_file)
+         with open(rc("cib-empty.xml")) as cib_file:
+             self.config.env.set_cib_data(cib_file.read(), cib_tempfile=tmp_file)
+-        (
+-            self.config.runner.cib.load(env=env)
+-            .raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                content=self.fixture_cfg_content(),
+-            )
+-            .runner.pcmk.load_agent(
+-                agent_name="ocf:heartbeat:IPaddr2",
+-                name="runner.pcmk.load_agent.ipaddr2",
+-                env=env,
+-            )
+-            .runner.pcmk.load_agent(
+-                agent_name="ocf:pacemaker:booth-site",
+-                name="runner.pcmk.load_agent.booth-site",
+-                env=env,
+-            )
+-            .env.push_cib(resources=self.fixture_cib_booth_group())
++        self.config.runner.cib.load(env=env)
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            content=self.fixture_cfg_content(),
+         )
+-        commands.create_in_cluster(
+-            self.env_assist.get_env(),
+-            self.site_ip,
++        self.config.runner.pcmk.load_agent(
++            agent_name="ocf:heartbeat:IPaddr2",
++            name="runner.pcmk.load_agent.ipaddr2",
++            env=env,
+         )
++        self.config.runner.pcmk.load_agent(
++            agent_name="ocf:pacemaker:booth-site",
++            name="runner.pcmk.load_agent.booth-site",
++            env=env,
++        )
++        self.config.env.push_cib(resources=self.fixture_cib_booth_group())
++        commands.create_in_cluster(self.env_assist.get_env(), self.site_ip)
+ 
+     def test_not_live_booth(self):
+         self.config.env.set_booth(
+@@ -1867,8 +1749,7 @@ class CreateInCluster(TestCase, FixtureMixin):
+         )
+         self.env_assist.assert_raise_library_error(
+             lambda: commands.create_in_cluster(
+-                self.env_assist.get_env(),
+-                self.site_ip,
++                self.env_assist.get_env(), self.site_ip
+             ),
+             [
+                 fixture.error(
+@@ -1883,19 +1764,15 @@ class CreateInCluster(TestCase, FixtureMixin):
+         )
+ 
+     def test_booth_resource_already_created(self):
+-        (
+-            self.config.runner.cib.load(
+-                resources=self.fixture_cib_booth_group()
+-            ).raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                content=self.fixture_cfg_content(),
+-            )
++        self.config.runner.cib.load(resources=self.fixture_cib_booth_group())
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            content=self.fixture_cfg_content(),
+         )
+         self.env_assist.assert_raise_library_error(
+             lambda: commands.create_in_cluster(
+-                self.env_assist.get_env(),
+-                self.site_ip,
++                self.env_assist.get_env(), self.site_ip
+             )
+         )
+         self.env_assist.assert_reports(
+@@ -1904,17 +1781,14 @@ class CreateInCluster(TestCase, FixtureMixin):
+ 
+     def test_booth_config_does_not_exist(self):
+         error = "an error"
+-        (
+-            self.config.runner.cib.load().raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                exception_msg=error,
+-            )
++        self.config.runner.cib.load().raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            exception_msg=error,
+         )
+         self.env_assist.assert_raise_library_error(
+             lambda: commands.create_in_cluster(
+-                self.env_assist.get_env(),
+-                self.site_ip,
++                self.env_assist.get_env(), self.site_ip
+             )
+         )
+         self.env_assist.assert_reports(
+@@ -1959,28 +1833,25 @@ class CreateInCluster(TestCase, FixtureMixin):
+         )
+ 
+     def test_booth_agent_missing(self):
+-        (
+-            self.config.runner.cib.load()
+-            .raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                content=self.fixture_cfg_content(),
+-            )
+-            .runner.pcmk.load_agent(
+-                agent_name="ocf:heartbeat:IPaddr2",
+-                name="runner.pcmk.load_agent.ipaddr2",
+-            )
+-            .runner.pcmk.load_agent(
+-                agent_name="ocf:pacemaker:booth-site",
+-                agent_is_missing=True,
+-                name="runner.pcmk.load_agent.booth-site",
+-                stderr=REASON,
+-            )
++        self.config.runner.cib.load()
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            content=self.fixture_cfg_content(),
++        )
++        self.config.runner.pcmk.load_agent(
++            agent_name="ocf:heartbeat:IPaddr2",
++            name="runner.pcmk.load_agent.ipaddr2",
++        )
++        self.config.runner.pcmk.load_agent(
++            agent_name="ocf:pacemaker:booth-site",
++            agent_is_missing=True,
++            name="runner.pcmk.load_agent.booth-site",
++            stderr=REASON,
+         )
+         self.env_assist.assert_raise_library_error(
+             lambda: commands.create_in_cluster(
+-                self.env_assist.get_env(),
+-                self.site_ip,
++                self.env_assist.get_env(), self.site_ip
+             )
+         )
+         self.env_assist.assert_reports(
+@@ -1993,30 +1864,28 @@ class CreateInCluster(TestCase, FixtureMixin):
+                 ),
+             ]
+         )
+-
+-    def test_agents_missing_forced(self):
+-        (
+-            self.config.runner.cib.load()
+-            .raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                content=self.fixture_cfg_content(),
+-            )
+-            .runner.pcmk.load_agent(
+-                agent_name="ocf:heartbeat:IPaddr2",
+-                agent_is_missing=True,
+-                name="runner.pcmk.load_agent.ipaddr2",
+-                stderr=REASON,
+-            )
+-            .runner.pcmk.load_agent(
+-                agent_name="ocf:pacemaker:booth-site",
+-                agent_is_missing=True,
+-                name="runner.pcmk.load_agent.booth-site",
+-                stderr=REASON,
+-            )
+-            .env.push_cib(
+-                resources=self.fixture_cib_booth_group(default_operations=True)
+-            )
++
++    def test_agents_missing_forced(self):
++        self.config.runner.cib.load()
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            content=self.fixture_cfg_content(),
++        )
++        self.config.runner.pcmk.load_agent(
++            agent_name="ocf:heartbeat:IPaddr2",
++            agent_is_missing=True,
++            name="runner.pcmk.load_agent.ipaddr2",
++            stderr=REASON,
++        )
++        self.config.runner.pcmk.load_agent(
++            agent_name="ocf:pacemaker:booth-site",
++            agent_is_missing=True,
++            name="runner.pcmk.load_agent.booth-site",
++            stderr=REASON,
++        )
++        self.config.env.push_cib(
++            resources=self.fixture_cib_booth_group(default_operations=True)
+         )
+         commands.create_in_cluster(
+             self.env_assist.get_env(),
+@@ -2061,10 +1930,9 @@ class RemoveFromCluster(TestCase, FixtureMixin):
+         self.resource_remove.assert_not_called()
+ 
+     def test_success_default_instance(self):
+-        (self.config.runner.cib.load(resources=self.fixture_cib_booth_group()))
++        self.config.runner.cib.load(resources=self.fixture_cib_booth_group())
+         commands.remove_from_cluster(
+-            self.env_assist.get_env(),
+-            self.resource_remove,
++            self.env_assist.get_env(), self.resource_remove
+         )
+         self.resource_remove.assert_has_calls(
+             [
+@@ -2075,10 +1943,8 @@ class RemoveFromCluster(TestCase, FixtureMixin):
+ 
+     def test_success_custom_instance(self):
+         instance_name = "my_booth"
+-        (
+-            self.config.runner.cib.load(
+-                resources=self.fixture_cib_booth_group(instance_name)
+-            )
++        self.config.runner.cib.load(
++            resources=self.fixture_cib_booth_group(instance_name)
+         )
+         commands.remove_from_cluster(
+             self.env_assist.get_env(),
+@@ -2099,16 +1965,12 @@ class RemoveFromCluster(TestCase, FixtureMixin):
+         cib_xml_man.append_to_first_tag_name(
+             "resources", self.fixture_cib_booth_group(wrap_in_resources=False)
+         )
+-        (
+-            self.config
+-            # This makes env.is_cib_live return False
+-            .env.set_cib_data(str(cib_xml_man), cib_tempfile=tmp_file)
+-            # This instructs the runner to actually return our mocked cib
+-            .runner.cib.load_content(str(cib_xml_man), env=env)
+-        )
++        # This makes env.is_cib_live return False
++        self.config.env.set_cib_data(str(cib_xml_man), cib_tempfile=tmp_file)
++        # This instructs the runner to actually return our mocked cib
++        self.config.runner.cib.load_content(str(cib_xml_man), env=env)
+         commands.remove_from_cluster(
+-            self.env_assist.get_env(),
+-            self.resource_remove,
++            self.env_assist.get_env(), self.resource_remove
+         )
+         self.resource_remove.assert_has_calls(
+             [
+@@ -2127,8 +1989,7 @@ class RemoveFromCluster(TestCase, FixtureMixin):
+         )
+         self.env_assist.assert_raise_library_error(
+             lambda: commands.remove_from_cluster(
+-                self.env_assist.get_env(),
+-                self.resource_remove,
++                self.env_assist.get_env(), self.resource_remove
+             ),
+             [
+                 fixture.error(
+@@ -2147,8 +2008,7 @@ class RemoveFromCluster(TestCase, FixtureMixin):
+         (self.config.runner.cib.load())
+         self.env_assist.assert_raise_library_error(
+             lambda: commands.remove_from_cluster(
+-                self.env_assist.get_env(),
+-                self.resource_remove,
++                self.env_assist.get_env(), self.resource_remove
+             ),
+         )
+         self.env_assist.assert_reports(
+@@ -2162,15 +2022,10 @@ class RemoveFromCluster(TestCase, FixtureMixin):
+         self.resource_remove.assert_not_called()
+ 
+     def test_more_booth_resources(self):
+-        (
+-            self.config.runner.cib.load(
+-                resources=self.fixture_cib_more_resources()
+-            )
+-        )
++        self.config.runner.cib.load(resources=self.fixture_cib_more_resources())
+         self.env_assist.assert_raise_library_error(
+             lambda: commands.remove_from_cluster(
+-                self.env_assist.get_env(),
+-                self.resource_remove,
++                self.env_assist.get_env(), self.resource_remove
+             ),
+         )
+         self.env_assist.assert_reports(
+@@ -2185,11 +2040,7 @@ class RemoveFromCluster(TestCase, FixtureMixin):
+         self.resource_remove.assert_not_called()
+ 
+     def test_more_booth_resources_forced(self):
+-        (
+-            self.config.runner.cib.load(
+-                resources=self.fixture_cib_more_resources()
+-            )
+-        )
++        self.config.runner.cib.load(resources=self.fixture_cib_more_resources())
+         commands.remove_from_cluster(
+             self.env_assist.get_env(),
+             self.resource_remove,
+@@ -2234,11 +2085,8 @@ class Restart(TestCase, FixtureMixin):
+         self.resource_restart.assert_not_called()
+ 
+     def test_success_default_instance(self):
+-        (self.config.runner.cib.load(resources=self.fixture_cib_booth_group()))
+-        commands.restart(
+-            self.env_assist.get_env(),
+-            self.resource_restart,
+-        )
++        self.config.runner.cib.load(resources=self.fixture_cib_booth_group())
++        commands.restart(self.env_assist.get_env(), self.resource_restart)
+         self.resource_restart.assert_has_calls(
+             [
+                 mock.call(["booth-booth-service"]),
+@@ -2247,10 +2095,8 @@ class Restart(TestCase, FixtureMixin):
+ 
+     def test_success_custom_instance(self):
+         instance_name = "my_booth"
+-        (
+-            self.config.runner.cib.load(
+-                resources=self.fixture_cib_booth_group(instance_name)
+-            )
++        self.config.runner.cib.load(
++            resources=self.fixture_cib_booth_group(instance_name)
+         )
+         commands.restart(
+             self.env_assist.get_env(),
+@@ -2274,8 +2120,7 @@ class Restart(TestCase, FixtureMixin):
+         self.config.env.set_cib_data("<cib />")
+         self.env_assist.assert_raise_library_error(
+             lambda: commands.restart(
+-                self.env_assist.get_env(),
+-                self.resource_restart,
++                self.env_assist.get_env(), self.resource_restart
+             ),
+             [
+                 fixture.error(
+@@ -2295,8 +2140,7 @@ class Restart(TestCase, FixtureMixin):
+         (self.config.runner.cib.load())
+         self.env_assist.assert_raise_library_error(
+             lambda: commands.restart(
+-                self.env_assist.get_env(),
+-                self.resource_restart,
++                self.env_assist.get_env(), self.resource_restart
+             ),
+         )
+         self.env_assist.assert_reports(
+@@ -2310,15 +2154,10 @@ class Restart(TestCase, FixtureMixin):
+         self.resource_restart.assert_not_called()
+ 
+     def test_more_booth_resources(self):
+-        (
+-            self.config.runner.cib.load(
+-                resources=self.fixture_cib_more_resources()
+-            )
+-        )
++        self.config.runner.cib.load(resources=self.fixture_cib_more_resources())
+         self.env_assist.assert_raise_library_error(
+             lambda: commands.restart(
+-                self.env_assist.get_env(),
+-                self.resource_restart,
++                self.env_assist.get_env(), self.resource_restart
+             ),
+         )
+         self.env_assist.assert_reports(
+@@ -2333,11 +2172,7 @@ class Restart(TestCase, FixtureMixin):
+         self.resource_restart.assert_not_called()
+ 
+     def test_more_booth_resources_forced(self):
+-        (
+-            self.config.runner.cib.load(
+-                resources=self.fixture_cib_more_resources()
+-            )
+-        )
++        self.config.runner.cib.load(resources=self.fixture_cib_more_resources())
+         commands.restart(
+             self.env_assist.get_env(),
+             self.resource_restart,
+@@ -2390,10 +2225,7 @@ class TicketGrantRevokeMixin(FixtureMixin):
+         )
+         self.config.env.set_cib_data("<cib/>")
+         self.env_assist.assert_raise_library_error(
+-            lambda: self.command(
+-                self.env_assist.get_env(),
+-                self.ticket,
+-            ),
++            lambda: self.command(self.env_assist.get_env(), self.ticket),
+             [
+                 fixture.error(
+                     reports.codes.LIVE_ENVIRONMENT_REQUIRED,
+@@ -2410,26 +2242,18 @@ class TicketGrantRevokeMixin(FixtureMixin):
+     def test_success_site_ip_specified(self):
+         self.get_booth_call()(self.ticket, self.site_ip)
+         self.command(
+-            self.env_assist.get_env(),
+-            self.ticket,
+-            site_ip=self.site_ip,
++            self.env_assist.get_env(), self.ticket, site_ip=self.site_ip
+         )
+ 
+     def test_success_site_ip_not_specified(self):
+         self.config.runner.cib.load(resources=self.fixture_cib_booth_group())
+         self.get_booth_call()(self.ticket, self.site_ip)
+-        self.command(
+-            self.env_assist.get_env(),
+-            self.ticket,
+-        )
++        self.command(self.env_assist.get_env(), self.ticket)
+ 
+     def test_cannot_find_site_ip(self):
+         self.config.runner.cib.load()
+         self.env_assist.assert_raise_library_error(
+-            lambda: self.command(
+-                self.env_assist.get_env(),
+-                self.ticket,
+-            ),
++            lambda: self.command(self.env_assist.get_env(), self.ticket),
+             [
+                 fixture.error(
+                     reports.codes.BOOTH_CANNOT_DETERMINE_LOCAL_SITE_IP,
+@@ -2448,9 +2272,7 @@ class TicketGrantRevokeMixin(FixtureMixin):
+         )
+         self.env_assist.assert_raise_library_error(
+             lambda: self.command(
+-                self.env_assist.get_env(),
+-                self.ticket,
+-                site_ip=self.site_ip,
++                self.env_assist.get_env(), self.ticket, site_ip=self.site_ip
+             ),
+             [
+                 fixture.error(
+@@ -2496,34 +2318,30 @@ class ConfigSyncTest(TestCase, FixtureMixin):
+             self.fixture_key_path(instance_name)
+         )
+         self.fixture_config_read_success(instance_name=instance_name)
+-        (
+-            self.config.http.booth.send_config(
+-                instance_name,
+-                config_content.decode("utf-8"),
+-                authfile=os.path.basename(self.fixture_key_path(instance_name)),
+-                authfile_data=RANDOM_KEY,
+-                node_labels=self.node_list,
+-            )
++        self.config.http.booth.send_config(
++            instance_name,
++            config_content.decode("utf-8"),
++            authfile=os.path.basename(self.fixture_key_path(instance_name)),
++            authfile_data=RANDOM_KEY,
++            node_labels=self.node_list,
+         )
+ 
+     def fixture_config_read_success(self, instance_name="booth"):
+         config_content = self.fixture_cfg_content(
+             self.fixture_key_path(instance_name)
+         )
+-        (
+-            self.config.corosync_conf.load()
+-            .raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(instance_name),
+-                content=config_content,
+-                name="raw_file.read.conf",
+-            )
+-            .raw_file.read(
+-                file_type_codes.BOOTH_KEY,
+-                self.fixture_key_path(instance_name),
+-                content=RANDOM_KEY,
+-                name="raw_file.read.key",
+-            )
++        self.config.corosync_conf.load()
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(instance_name),
++            content=config_content,
++            name="raw_file.read.conf",
++        )
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_KEY,
++            self.fixture_key_path(instance_name),
++            content=RANDOM_KEY,
++            name="raw_file.read.key",
+         )
+ 
+     def fixture_reports_success(self, instance_name="booth"):
+@@ -2552,30 +2370,23 @@ class ConfigSyncTest(TestCase, FixtureMixin):
+ 
+     def test_success_default_instance(self):
+         self.fixture_config_success()
+-        commands.config_sync(
+-            self.env_assist.get_env(),
+-        )
++        commands.config_sync(self.env_assist.get_env())
+         self.env_assist.assert_reports(self.fixture_reports_success())
+ 
+     def test_success_custom_instance(self):
+         instance_name = "my_booth"
+         self.fixture_config_success(instance_name=instance_name)
+         commands.config_sync(
+-            self.env_assist.get_env(),
+-            instance_name=instance_name,
++            self.env_assist.get_env(), instance_name=instance_name
+         )
+         self.env_assist.assert_reports(
+-            self.fixture_reports_success(
+-                instance_name=instance_name,
+-            )
++            self.fixture_reports_success(instance_name=instance_name)
+         )
+ 
+     def test_not_live_cib(self):
+         self.config.env.set_cib_data("<cib/>")
+         self.env_assist.assert_raise_library_error(
+-            lambda: commands.config_sync(
+-                self.env_assist.get_env(),
+-            ),
++            lambda: commands.config_sync(self.env_assist.get_env()),
+             [
+                 fixture.error(
+                     reports.codes.LIVE_ENVIRONMENT_REQUIRED,
+@@ -2596,56 +2407,45 @@ class ConfigSyncTest(TestCase, FixtureMixin):
+                 "key_path": "some key path",
+             }
+         )
+-        (
+-            self.config.corosync_conf.load(
+-                node_name_list=self.node_list
+-            ).http.booth.send_config(
+-                instance_name,
+-                config_data.decode("utf-8"),
+-                authfile=os.path.basename(key_path),
+-                authfile_data=key_data,
+-                node_labels=self.node_list,
+-            )
++        self.config.corosync_conf.load(node_name_list=self.node_list)
++        self.config.http.booth.send_config(
++            instance_name,
++            config_data.decode("utf-8"),
++            authfile=os.path.basename(key_path),
++            authfile_data=key_data,
++            node_labels=self.node_list,
+         )
+ 
+     def test_not_live_booth_default_instance(self):
+         self.fixture_config_success_not_live()
+-        commands.config_sync(
+-            self.env_assist.get_env(),
+-        )
++        commands.config_sync(self.env_assist.get_env())
+         self.env_assist.assert_reports(self.fixture_reports_success())
+ 
+     def test_not_live_booth_custom_instance(self):
+         instance_name = "my_booth"
+         self.fixture_config_success_not_live(instance_name=instance_name)
+         commands.config_sync(
+-            self.env_assist.get_env(),
+-            instance_name=instance_name,
++            self.env_assist.get_env(), instance_name=instance_name
+         )
+         self.env_assist.assert_reports(
+-            self.fixture_reports_success(
+-                instance_name=instance_name,
+-            )
++            self.fixture_reports_success(instance_name=instance_name)
+         )
+ 
+     def test_some_node_names_missing(self):
+         nodes = ["rh7-2"]
+         self.fixture_config_read_success()
+-        (
+-            self.config.corosync_conf.load(
+-                filename="corosync-some-node-names.conf",
+-                instead="corosync_conf.load",
+-            ).http.booth.send_config(
+-                "booth",
+-                self.fixture_cfg_content().decode("utf-8"),
+-                authfile=os.path.basename(self.fixture_key_path()),
+-                authfile_data=RANDOM_KEY,
+-                node_labels=nodes,
+-            )
++        self.config.corosync_conf.load(
++            filename="corosync-some-node-names.conf",
++            instead="corosync_conf.load",
+         )
+-        commands.config_sync(
+-            self.env_assist.get_env(),
++        self.config.http.booth.send_config(
++            "booth",
++            self.fixture_cfg_content().decode("utf-8"),
++            authfile=os.path.basename(self.fixture_key_path()),
++            authfile_data=RANDOM_KEY,
++            node_labels=nodes,
+         )
++        commands.config_sync(self.env_assist.get_env())
+         self.env_assist.assert_reports(
+             [
+                 fixture.info(reports.codes.BOOTH_CONFIG_DISTRIBUTION_STARTED),
+@@ -2666,11 +2466,9 @@ class ConfigSyncTest(TestCase, FixtureMixin):
+ 
+     def test_all_node_names_missing(self):
+         self.fixture_config_read_success()
+-        (
+-            self.config.corosync_conf.load(
+-                filename="corosync-no-node-names.conf",
+-                instead="corosync_conf.load",
+-            )
++        self.config.corosync_conf.load(
++            filename="corosync-no-node-names.conf",
++            instead="corosync_conf.load",
+         )
+         self.env_assist.assert_raise_library_error(
+             lambda: commands.config_sync(self.env_assist.get_env())
+@@ -2687,23 +2485,21 @@ class ConfigSyncTest(TestCase, FixtureMixin):
+ 
+     def test_node_failure(self):
+         self.fixture_config_read_success()
+-        (
+-            self.config.http.booth.send_config(
+-                "booth",
+-                self.fixture_cfg_content().decode("utf-8"),
+-                authfile=os.path.basename(self.fixture_key_path()),
+-                authfile_data=RANDOM_KEY,
+-                communication_list=[
+-                    dict(
+-                        label=self.node_list[0],
+-                        response_code=400,
+-                        output=self.reason,
+-                    ),
+-                    dict(
+-                        label=self.node_list[1],
+-                    ),
+-                ],
+-            )
++        self.config.http.booth.send_config(
++            "booth",
++            self.fixture_cfg_content().decode("utf-8"),
++            authfile=os.path.basename(self.fixture_key_path()),
++            authfile_data=RANDOM_KEY,
++            communication_list=[
++                dict(
++                    label=self.node_list[0],
++                    response_code=400,
++                    output=self.reason,
++                ),
++                dict(
++                    label=self.node_list[1],
++                ),
++            ],
+         )
+         self.env_assist.assert_raise_library_error(
+             lambda: commands.config_sync(self.env_assist.get_env()), []
+@@ -2728,23 +2524,21 @@ class ConfigSyncTest(TestCase, FixtureMixin):
+ 
+     def test_node_failure_skip_offline(self):
+         self.fixture_config_read_success()
+-        (
+-            self.config.http.booth.send_config(
+-                "booth",
+-                self.fixture_cfg_content().decode("utf-8"),
+-                authfile=os.path.basename(self.fixture_key_path()),
+-                authfile_data=RANDOM_KEY,
+-                communication_list=[
+-                    dict(
+-                        label=self.node_list[0],
+-                        response_code=400,
+-                        output=self.reason,
+-                    ),
+-                    dict(
+-                        label=self.node_list[1],
+-                    ),
+-                ],
+-            )
++        self.config.http.booth.send_config(
++            "booth",
++            self.fixture_cfg_content().decode("utf-8"),
++            authfile=os.path.basename(self.fixture_key_path()),
++            authfile_data=RANDOM_KEY,
++            communication_list=[
++                dict(
++                    label=self.node_list[0],
++                    response_code=400,
++                    output=self.reason,
++                ),
++                dict(
++                    label=self.node_list[1],
++                ),
++            ],
+         )
+ 
+         commands.config_sync(self.env_assist.get_env(), skip_offline_nodes=True)
+@@ -2767,24 +2561,22 @@ class ConfigSyncTest(TestCase, FixtureMixin):
+ 
+     def test_node_offline(self):
+         self.fixture_config_read_success()
+-        (
+-            self.config.http.booth.send_config(
+-                "booth",
+-                self.fixture_cfg_content().decode("utf-8"),
+-                authfile=os.path.basename(self.fixture_key_path()),
+-                authfile_data=RANDOM_KEY,
+-                communication_list=[
+-                    dict(
+-                        label=self.node_list[0],
+-                        errno=1,
+-                        error_msg=self.reason,
+-                        was_connected=False,
+-                    ),
+-                    dict(
+-                        label=self.node_list[1],
+-                    ),
+-                ],
+-            )
++        self.config.http.booth.send_config(
++            "booth",
++            self.fixture_cfg_content().decode("utf-8"),
++            authfile=os.path.basename(self.fixture_key_path()),
++            authfile_data=RANDOM_KEY,
++            communication_list=[
++                dict(
++                    label=self.node_list[0],
++                    errno=1,
++                    error_msg=self.reason,
++                    was_connected=False,
++                ),
++                dict(
++                    label=self.node_list[1],
++                ),
++            ],
+         )
+ 
+         self.env_assist.assert_raise_library_error(
+@@ -2810,24 +2602,22 @@ class ConfigSyncTest(TestCase, FixtureMixin):
+ 
+     def test_node_offline_skip_offline(self):
+         self.fixture_config_read_success()
+-        (
+-            self.config.http.booth.send_config(
+-                "booth",
+-                self.fixture_cfg_content().decode("utf-8"),
+-                authfile=os.path.basename(self.fixture_key_path()),
+-                authfile_data=RANDOM_KEY,
+-                communication_list=[
+-                    dict(
+-                        label=self.node_list[0],
+-                        errno=1,
+-                        error_msg=self.reason,
+-                        was_connected=False,
+-                    ),
+-                    dict(
+-                        label=self.node_list[1],
+-                    ),
+-                ],
+-            )
++        self.config.http.booth.send_config(
++            "booth",
++            self.fixture_cfg_content().decode("utf-8"),
++            authfile=os.path.basename(self.fixture_key_path()),
++            authfile_data=RANDOM_KEY,
++            communication_list=[
++                dict(
++                    label=self.node_list[0],
++                    errno=1,
++                    error_msg=self.reason,
++                    was_connected=False,
++                ),
++                dict(
++                    label=self.node_list[1],
++                ),
++            ],
+         )
+ 
+         commands.config_sync(self.env_assist.get_env(), skip_offline_nodes=True)
+@@ -2849,15 +2639,13 @@ class ConfigSyncTest(TestCase, FixtureMixin):
+         )
+ 
+     def test_config_not_accessible(self):
+-        (
+-            self.config.corosync_conf.load().raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                exception_msg=self.reason,
+-            )
++        self.config.corosync_conf.load().raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            exception_msg=self.reason,
+         )
+         self.env_assist.assert_raise_library_error(
+-            lambda: commands.config_sync(self.env_assist.get_env()),
++            lambda: commands.config_sync(self.env_assist.get_env())
+         )
+         self.env_assist.assert_reports(
+             [
+@@ -2881,7 +2669,7 @@ class ConfigSyncTest(TestCase, FixtureMixin):
+         )
+         (self.config.corosync_conf.load())
+         self.env_assist.assert_raise_library_error(
+-            lambda: commands.config_sync(self.env_assist.get_env()),
++            lambda: commands.config_sync(self.env_assist.get_env())
+         )
+         self.env_assist.assert_reports(
+             [
+@@ -2896,15 +2684,13 @@ class ConfigSyncTest(TestCase, FixtureMixin):
+         )
+ 
+     def test_config_parse_error(self):
+-        (
+-            self.config.corosync_conf.load().raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                content="invalid config".encode("utf-8"),
+-            )
++        self.config.corosync_conf.load().raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            content="invalid config".encode("utf-8"),
+         )
+         self.env_assist.assert_raise_library_error(
+-            lambda: commands.config_sync(self.env_assist.get_env()),
++            lambda: commands.config_sync(self.env_assist.get_env())
+         )
+         self.env_assist.assert_reports(
+             [
+@@ -2924,9 +2710,9 @@ class ConfigSyncTest(TestCase, FixtureMixin):
+                 "key_path": "some key path",
+             }
+         )
+-        (self.config.corosync_conf.load())
++        self.config.corosync_conf.load()
+         self.env_assist.assert_raise_library_error(
+-            lambda: commands.config_sync(self.env_assist.get_env()),
++            lambda: commands.config_sync(self.env_assist.get_env())
+         )
+         self.env_assist.assert_reports(
+             [
+@@ -2939,23 +2725,21 @@ class ConfigSyncTest(TestCase, FixtureMixin):
+         )
+ 
+     def test_authfile_not_accessible(self):
+-        (
+-            self.config.corosync_conf.load()
+-            .raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                content=self.fixture_cfg_content(),
+-                name="raw_file.read.conf",
+-            )
+-            .raw_file.read(
+-                file_type_codes.BOOTH_KEY,
+-                self.fixture_key_path(),
+-                exception_msg=self.reason,
+-                name="raw_file.read.key",
+-            )
++        self.config.corosync_conf.load()
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            content=self.fixture_cfg_content(),
++            name="raw_file.read.conf",
++        )
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_KEY,
++            self.fixture_key_path(),
++            exception_msg=self.reason,
++            name="raw_file.read.key",
+         )
+         self.env_assist.assert_raise_library_error(
+-            lambda: commands.config_sync(self.env_assist.get_env()),
++            lambda: commands.config_sync(self.env_assist.get_env())
+         )
+         self.env_assist.assert_reports(
+             [
+@@ -2977,9 +2761,9 @@ class ConfigSyncTest(TestCase, FixtureMixin):
+                 "key_path": "some key path",
+             }
+         )
+-        (self.config.corosync_conf.load())
++        self.config.corosync_conf.load()
+         self.env_assist.assert_raise_library_error(
+-            lambda: commands.config_sync(self.env_assist.get_env()),
++            lambda: commands.config_sync(self.env_assist.get_env())
+         )
+         self.env_assist.assert_reports(
+             [
+@@ -2994,22 +2778,22 @@ class ConfigSyncTest(TestCase, FixtureMixin):
+         )
+ 
+     def test_no_authfile(self):
+-        (
+-            self.config.corosync_conf.load()
+-            .raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                content=bytes(),
+-            )
+-            .http.booth.send_config(
+-                "booth",
+-                bytes().decode("utf-8"),
+-                node_labels=self.node_list,
+-            )
++        self.config.corosync_conf.load()
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            content=bytes(),
++        )
++        self.config.http.booth.send_config(
++            "booth",
++            bytes().decode("utf-8"),
++            node_labels=self.node_list,
+         )
+         commands.config_sync(self.env_assist.get_env())
+         self.env_assist.assert_reports(
+-            [fixture.info(reports.codes.BOOTH_CONFIG_DISTRIBUTION_STARTED)]
++            [
++                fixture.info(reports.codes.BOOTH_CONFIG_DISTRIBUTION_STARTED),
++            ]
+             + [
+                 fixture.info(
+                     reports.codes.BOOTH_CONFIG_ACCEPTED_BY_NODE,
+@@ -3022,18 +2806,14 @@ class ConfigSyncTest(TestCase, FixtureMixin):
+ 
+     def test_authfile_not_in_booth_dir(self):
+         config_content = "authfile=/etc/my_booth.key"
+-        (
+-            self.config.corosync_conf.load()
+-            .raw_file.read(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.fixture_cfg_path(),
+-                content=config_content.encode("utf-8"),
+-            )
+-            .http.booth.send_config(
+-                "booth",
+-                config_content,
+-                node_labels=self.node_list,
+-            )
++        self.config.corosync_conf.load()
++        self.config.raw_file.read(
++            file_type_codes.BOOTH_CONFIG,
++            self.fixture_cfg_path(),
++            content=config_content.encode("utf-8"),
++        )
++        self.config.http.booth.send_config(
++            "booth", config_content, node_labels=self.node_list
+         )
+         commands.config_sync(self.env_assist.get_env())
+         self.env_assist.assert_reports(
+@@ -3096,9 +2876,7 @@ class EnableDisableStartStopMixin(FixtureMixin):
+         )
+         self.config.env.set_cib_data("<cib/>")
+         self.env_assist.assert_raise_library_error(
+-            lambda: self.command(
+-                self.env_assist.get_env(),
+-            ),
++            lambda: self.command(self.env_assist.get_env()),
+             [
+                 fixture.error(
+                     reports.codes.LIVE_ENVIRONMENT_REQUIRED,
+@@ -3114,9 +2892,7 @@ class EnableDisableStartStopMixin(FixtureMixin):
+ 
+     def test_success_default_instance(self):
+         self.get_external_call()("booth", instance="booth")
+-        self.command(
+-            self.env_assist.get_env(),
+-        )
++        self.command(self.env_assist.get_env())
+         self.env_assist.assert_reports(
+             [
+                 fixture.info(
+@@ -3149,9 +2925,7 @@ class EnableDisableStartStopMixin(FixtureMixin):
+         err_msg = "some stderr\nsome stdout"
+         self.get_external_call()("booth", instance="booth", failure_msg=err_msg)
+         self.env_assist.assert_raise_library_error(
+-            lambda: self.command(
+-                self.env_assist.get_env(),
+-            ),
++            lambda: self.command(self.env_assist.get_env()),
+             [
+                 fixture.error(
+                     reports.codes.SERVICE_ACTION_FAILED,
+@@ -3226,17 +3000,16 @@ class PullConfigBase(TestCase, FixtureMixin):
+ class PullConfigSuccess(PullConfigBase):
+     def setUp(self):
+         super().setUp()
+-        (
+-            self.config.http.booth.get_config(
+-                self.name,
+-                self.config_data.decode("utf-8"),
+-                node_labels=[self.node_name],
+-            ).raw_file.write(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.config_path,
+-                self.config_data,
+-                can_overwrite=True,
+-            )
++        self.config.http.booth.get_config(
++            self.name,
++            self.config_data.decode("utf-8"),
++            node_labels=[self.node_name],
++        )
++        self.config.raw_file.write(
++            file_type_codes.BOOTH_CONFIG,
++            self.config_path,
++            self.config_data,
++            can_overwrite=True,
+         )
+ 
+     def test_success(self):
+@@ -3265,17 +3038,16 @@ class PullConfigSuccessCustomInstance(TestCase, FixtureMixin):
+         ]
+ 
+     def test_success(self):
+-        (
+-            self.config.http.booth.get_config(
+-                self.name,
+-                self.config_data.decode("utf-8"),
+-                node_labels=[self.node_name],
+-            ).raw_file.write(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.config_path,
+-                self.config_data,
+-                can_overwrite=True,
+-            )
++        self.config.http.booth.get_config(
++            self.name,
++            self.config_data.decode("utf-8"),
++            node_labels=[self.node_name],
++        )
++        self.config.raw_file.write(
++            file_type_codes.BOOTH_CONFIG,
++            self.config_path,
++            self.config_data,
++            can_overwrite=True,
+         )
+         commands.pull_config(
+             self.env_assist.get_env(),
+@@ -3514,34 +3286,31 @@ class PullConfigWithAuthfile(PullConfigBase):
+         self.authfile = os.path.basename(self.authfile_path)
+         self.authfile_data = b"auth"
+ 
+-        (
+-            self.config.http.booth.get_config(
+-                self.name,
+-                self.config_data.decode("utf-8"),
+-                authfile=self.authfile,
+-                authfile_data=self.authfile_data,
+-                node_labels=[self.node_name],
+-            )
++        self.config.http.booth.get_config(
++            self.name,
++            self.config_data.decode("utf-8"),
++            authfile=self.authfile,
++            authfile_data=self.authfile_data,
++            node_labels=[self.node_name],
+         )
+ 
+ 
+ class PullConfigWithAuthfileSuccess(PullConfigWithAuthfile):
+     def setUp(self):
+         super().setUp()
+-        (
+-            self.config.raw_file.write(
+-                file_type_codes.BOOTH_KEY,
+-                self.authfile_path,
+-                self.authfile_data,
+-                can_overwrite=True,
+-                name="raw_file.write.key",
+-            ).raw_file.write(
+-                file_type_codes.BOOTH_CONFIG,
+-                self.config_path,
+-                self.config_data,
+-                can_overwrite=True,
+-                name="raw_file.write.cfg",
+-            )
++        self.config.raw_file.write(
++            file_type_codes.BOOTH_KEY,
++            self.authfile_path,
++            self.authfile_data,
++            can_overwrite=True,
++            name="raw_file.write.key",
++        )
++        self.config.raw_file.write(
++            file_type_codes.BOOTH_CONFIG,
++            self.config_path,
++            self.config_data,
++            can_overwrite=True,
++            name="raw_file.write.cfg",
+         )
+ 
+     def test_success(self):
+@@ -3642,13 +3411,13 @@ class GetStatus(TestCase):
+ 
+     def assert_success(self, instance_name=None):
+         inner_name = instance_name or "booth"
+-        (
+-            self.config.runner.booth.status_daemon(
+-                inner_name, stdout="daemon status"
+-            )
+-            .runner.booth.status_tickets(inner_name, stdout="tickets status")
+-            .runner.booth.status_peers(inner_name, stdout="peers status")
++        self.config.runner.booth.status_daemon(
++            inner_name, stdout="daemon status"
+         )
++        self.config.runner.booth.status_tickets(
++            inner_name, stdout="tickets status"
++        )
++        self.config.runner.booth.status_peers(inner_name, stdout="peers status")
+         self.assertEqual(
+             commands.get_status(
+                 self.env_assist.get_env(), instance_name=instance_name
+@@ -3667,10 +3436,8 @@ class GetStatus(TestCase):
+         self.assert_success(instance_name="my_booth")
+ 
+     def test_daemon_status_failure(self):
+-        (
+-            self.config.runner.booth.status_daemon(
+-                "booth", stdout="some output", stderr="some error", returncode=1
+-            )
++        self.config.runner.booth.status_daemon(
++            "booth", stdout="some output", stderr="some error", returncode=1
+         )
+         self.env_assist.assert_raise_library_error(
+             lambda: commands.get_status(self.env_assist.get_env()),
+@@ -3684,12 +3451,9 @@ class GetStatus(TestCase):
+         )
+ 
+     def test_ticket_status_failure(self):
+-        (
+-            self.config.runner.booth.status_daemon(
+-                "booth", stdout="daemon status"
+-            ).runner.booth.status_tickets(
+-                "booth", stdout="some output", stderr="some error", returncode=1
+-            )
++        self.config.runner.booth.status_daemon("booth", stdout="daemon status")
++        self.config.runner.booth.status_tickets(
++            "booth", stdout="some output", stderr="some error", returncode=1
+         )
+         self.env_assist.assert_raise_library_error(
+             lambda: commands.get_status(self.env_assist.get_env()),
+@@ -3703,14 +3467,12 @@ class GetStatus(TestCase):
+         )
+ 
+     def test_peers_status_failure(self):
+-        (
+-            self.config.runner.booth.status_daemon(
+-                "booth", stdout="daemon status"
+-            )
+-            .runner.booth.status_tickets("booth", stdout="tickets status")
+-            .runner.booth.status_peers(
+-                "booth", stdout="some output", stderr="some error", returncode=1
+-            )
++        self.config.runner.booth.status_daemon("booth", stdout="daemon status")
++        self.config.runner.booth.status_tickets(
++            "booth", stdout="tickets status"
++        )
++        self.config.runner.booth.status_peers(
++            "booth", stdout="some output", stderr="some error", returncode=1
+         )
+         self.env_assist.assert_raise_library_error(
+             lambda: commands.get_status(self.env_assist.get_env()),
+-- 
+2.35.3
+
diff --git a/SOURCES/bz1786964-02-make-booth-ticket-mode-value-case-insensitive.patch b/SOURCES/bz1786964-02-make-booth-ticket-mode-value-case-insensitive.patch
new file mode 100644
index 0000000..58ce55a
--- /dev/null
+++ b/SOURCES/bz1786964-02-make-booth-ticket-mode-value-case-insensitive.patch
@@ -0,0 +1,126 @@
+From d6258ba9643b4d7528ceff65d433024104942a4c Mon Sep 17 00:00:00 2001
+From: Tomas Jelinek <tojeline@redhat.com>
+Date: Thu, 14 Jul 2022 16:46:05 +0200
+Subject: [PATCH 2/4] make booth ticket mode value case insensitive
+
+---
+ pcs/lib/booth/config_validators.py        | 10 ++++++++
+ pcs/lib/commands/booth.py                 | 14 +++++++++---
+ pcs_test/tier0/lib/commands/test_booth.py | 28 ++++++++++++++++-------
+ 3 files changed, 41 insertions(+), 11 deletions(-)
+
+diff --git a/pcs/lib/booth/config_validators.py b/pcs/lib/booth/config_validators.py
+index 99badc46..6c4a4ddc 100644
+--- a/pcs/lib/booth/config_validators.py
++++ b/pcs/lib/booth/config_validators.py
+@@ -100,6 +100,16 @@ def remove_ticket(conf_facade, ticket_name):
+     return []
+ 
+ 
++def ticket_options_normalization() -> validate.TypeNormalizeFunc:
++    return validate.option_value_normalization(
++        {
++            "mode": (
++                lambda value: value.lower() if isinstance(value, str) else value
++            )
++        }
++    )
++
++
+ def validate_ticket_name(ticket_name: str) -> reports.ReportItemList:
+     if not __TICKET_NAME_RE.search(ticket_name):
+         return [
+diff --git a/pcs/lib/commands/booth.py b/pcs/lib/commands/booth.py
+index e7891fbe..fc1454ce 100644
+--- a/pcs/lib/commands/booth.py
++++ b/pcs/lib/commands/booth.py
+@@ -23,7 +23,10 @@ from pcs.common.reports.item import (
+ )
+ from pcs.common.services.errors import ManageServiceError
+ from pcs.common.str_tools import join_multilines
+-from pcs.lib import tools
++from pcs.lib import (
++    tools,
++    validate,
++)
+ from pcs.lib.booth import (
+     config_files,
+     config_validators,
+@@ -329,17 +332,22 @@ def config_ticket_add(
+     booth_env = env.get_booth_env(instance_name)
+     try:
+         booth_conf = booth_env.config.read_to_facade()
++        options_pairs = validate.values_to_pairs(
++            options, config_validators.ticket_options_normalization()
++        )
+         report_processor.report_list(
+             config_validators.add_ticket(
+                 booth_conf,
+                 ticket_name,
+-                options,
++                options_pairs,
+                 allow_unknown_options=allow_unknown_options,
+             )
+         )
+         if report_processor.has_errors:
+             raise LibraryError()
+-        booth_conf.add_ticket(ticket_name, options)
++        booth_conf.add_ticket(
++            ticket_name, validate.pairs_to_values(options_pairs)
++        )
+         booth_env.config.write_facade(booth_conf, can_overwrite=True)
+     except RawFileError as e:
+         report_processor.report(raw_file_error_report(e))
+diff --git a/pcs_test/tier0/lib/commands/test_booth.py b/pcs_test/tier0/lib/commands/test_booth.py
+index 2b20a199..12b169c2 100644
+--- a/pcs_test/tier0/lib/commands/test_booth.py
++++ b/pcs_test/tier0/lib/commands/test_booth.py
+@@ -1194,7 +1194,7 @@ class ConfigTicketAdd(TestCase, FixtureMixin):
+             },
+         )
+ 
+-    def test_success_ticket_options(self):
++    def assert_success_ticket_options(self, options_command, options_config):
+         self.config.raw_file.read(
+             file_type_codes.BOOTH_CONFIG,
+             self.fixture_cfg_path(),
+@@ -1203,17 +1203,29 @@ class ConfigTicketAdd(TestCase, FixtureMixin):
+         self.config.raw_file.write(
+             file_type_codes.BOOTH_CONFIG,
+             self.fixture_cfg_path(),
+-            self.fixture_cfg_content(
+-                ticket_list=[
+-                    ["ticketA", [("retries", "10"), ("timeout", "20")]]
+-                ]
+-            ),
++            self.fixture_cfg_content(ticket_list=[["ticketA", options_config]]),
+             can_overwrite=True,
+         )
+         commands.config_ticket_add(
+-            self.env_assist.get_env(),
+-            "ticketA",
++            self.env_assist.get_env(), "ticketA", options_command
++        )
++
++    def test_success_ticket_options(self):
++        self.assert_success_ticket_options(
+             {"timeout": "20", "retries": "10"},
++            [("retries", "10"), ("timeout", "20")],
++        )
++
++    def test_success_ticket_options_mode(self):
++        self.assert_success_ticket_options(
++            {"timeout": "20", "retries": "10", "mode": "manual"},
++            [("mode", "manual"), ("retries", "10"), ("timeout", "20")],
++        )
++
++    def test_success_ticket_options_mode_case_insensitive(self):
++        self.assert_success_ticket_options(
++            {"timeout": "20", "retries": "10", "mode": "MaNuAl"},
++            [("mode", "manual"), ("retries", "10"), ("timeout", "20")],
+         )
+ 
+     def test_ticket_already_exists(self):
+-- 
+2.35.3
+
diff --git a/SOURCES/bz1791670-01-booth-sync-check-whether-etc-booth-exists.patch b/SOURCES/bz1791670-01-booth-sync-check-whether-etc-booth-exists.patch
new file mode 100644
index 0000000..cfbfff6
--- /dev/null
+++ b/SOURCES/bz1791670-01-booth-sync-check-whether-etc-booth-exists.patch
@@ -0,0 +1,46 @@
+From 7e44b3cd51a3a5079d0d42d91a3445f3b8ae9d17 Mon Sep 17 00:00:00 2001
+From: Tomas Jelinek <tojeline@redhat.com>
+Date: Fri, 15 Jul 2022 15:55:57 +0200
+Subject: [PATCH 3/4] booth sync: check whether /etc/booth exists
+
+---
+ pcsd/pcsd_file.rb | 6 +-----
+ pcsd/remote.rb    | 4 ++++
+ 2 files changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/pcsd/pcsd_file.rb b/pcsd/pcsd_file.rb
+index d82b55d2..394db59a 100644
+--- a/pcsd/pcsd_file.rb
++++ b/pcsd/pcsd_file.rb
+@@ -112,12 +112,8 @@ module PcsdFile
+       end
+     end
+ 
+-    def dir()
+-      return BOOTH_CONFIG_DIR
+-    end
+-
+     def full_file_name()
+-      @full_file_name ||= File.join(self.dir, @file[:name])
++      @full_file_name ||= File.join(BOOTH_CONFIG_DIR, @file[:name])
+     end
+   end
+ 
+diff --git a/pcsd/remote.rb b/pcsd/remote.rb
+index 9bf96db9..b7bee7e6 100644
+--- a/pcsd/remote.rb
++++ b/pcsd/remote.rb
+@@ -2622,6 +2622,10 @@ def booth_set_config(params, request, auth_user)
+     check_permissions(auth_user, Permissions::WRITE)
+     data = check_request_data_for_json(params, auth_user)
+ 
++    if not File.directory?(BOOTH_CONFIG_DIR)
++      raise "Configuration directory for booth '/etc/booth' is missing. Is booth installed?"
++    end
++
+     PcsdExchangeFormat::validate_item_map_is_Hash('files', data)
+     PcsdExchangeFormat::validate_item_is_Hash('file', :config, data[:config])
+     if data[:authfile]
+-- 
+2.35.3
+
diff --git a/SOURCES/bz1990784-01-Multiple-fixes-of-pcs-resource-move-autodelete-comma.patch b/SOURCES/bz1990784-01-Multiple-fixes-of-pcs-resource-move-autodelete-comma.patch
deleted file mode 100644
index ae32aae..0000000
--- a/SOURCES/bz1990784-01-Multiple-fixes-of-pcs-resource-move-autodelete-comma.patch
+++ /dev/null
@@ -1,1031 +0,0 @@
-From fe1ad27f32e69e3e7c046b51e5406a0693ea1c35 Mon Sep 17 00:00:00 2001
-From: Ondrej Mular <omular@redhat.com>
-Date: Tue, 11 Jan 2022 08:01:10 +0100
-Subject: [PATCH 3/5] Multiple fixes of `pcs resource move --autodelete`
- command
-
----
- pcs/common/reports/codes.py                   |   1 +
- pcs/common/reports/messages.py                |  21 ++
- pcs/lib/cib/node.py                           |  14 +-
- pcs/lib/commands/resource.py                  | 105 ++++++-
- pcs/lib/node.py                               |   7 +-
- .../tier0/common/reports/test_messages.py     |  12 +
- .../resource/test_resource_move_autoclean.py  | 280 +++++++++++++++++-
- .../resource/test_resource_move_ban.py        |  45 ++-
- .../tools/command_env/config_runner_pcmk.py   |   2 +
- pcs_test/tools/command_env/mock_runner.py     |   2 +-
- pcs_test/tools/fixture_cib.py                 |   1 +
- 11 files changed, 456 insertions(+), 34 deletions(-)
-
-diff --git a/pcs/common/reports/codes.py b/pcs/common/reports/codes.py
-index 5bae7170..3e0512d9 100644
---- a/pcs/common/reports/codes.py
-+++ b/pcs/common/reports/codes.py
-@@ -418,6 +418,7 @@ RESOURCE_UNMOVE_UNBAN_PCMK_EXPIRED_NOT_SUPPORTED = M(
- )
- RESOURCE_MOVE_CONSTRAINT_CREATED = M("RESOURCE_MOVE_CONSTRAINT_CREATED")
- RESOURCE_MOVE_CONSTRAINT_REMOVED = M("RESOURCE_MOVE_CONSTRAINT_REMOVED")
-+RESOURCE_MOVE_NOT_AFFECTING_RESOURCE = M("RESOURCE_MOVE_NOT_AFFECTING_RESOURCE")
- RESOURCE_MOVE_AFFECTS_OTRHER_RESOURCES = M(
-     "RESOURCE_MOVE_AFFECTS_OTRHER_RESOURCES"
- )
-diff --git a/pcs/common/reports/messages.py b/pcs/common/reports/messages.py
-index 43ce38e1..9d665e73 100644
---- a/pcs/common/reports/messages.py
-+++ b/pcs/common/reports/messages.py
-@@ -6110,6 +6110,27 @@ class ResourceMoveConstraintRemoved(ReportItemMessage):
-         )
- 
- 
-+@dataclass(frozen=True)
-+class ResourceMoveNotAffectingResource(ReportItemMessage):
-+    """
-+    Creating a location constraint to move a resource has no effect on the
-+    resource.
-+
-+    resource_id -- id of the resource to be moved
-+    """
-+
-+    resource_id: str
-+    _code = codes.RESOURCE_MOVE_NOT_AFFECTING_RESOURCE
-+
-+    @property
-+    def message(self) -> str:
-+        return (
-+            f"Unable to move resource '{self.resource_id}' using a location "
-+            "constraint. Current location of the resource may be affected by "
-+            "some other constraint."
-+        )
-+
-+
- @dataclass(frozen=True)
- class ResourceMoveAffectsOtherResources(ReportItemMessage):
-     """
-diff --git a/pcs/lib/cib/node.py b/pcs/lib/cib/node.py
-index 20a41ca0..df2ffbaa 100644
---- a/pcs/lib/cib/node.py
-+++ b/pcs/lib/cib/node.py
-@@ -1,12 +1,17 @@
- from collections import namedtuple
-+from typing import Set
- from lxml import etree
-+from lxml.etree import _Element
- 
- from pcs.common import reports
- from pcs.common.reports.item import ReportItem
- from pcs.lib.cib.nvpair import update_nvset
- from pcs.lib.cib.tools import get_nodes
- from pcs.lib.errors import LibraryError
--from pcs.lib.xml_tools import append_when_useful
-+from pcs.lib.xml_tools import (
-+    append_when_useful,
-+    get_root,
-+)
- 
- 
- class PacemakerNode(namedtuple("PacemakerNode", "name addr")):
-@@ -58,6 +63,13 @@ def update_node_instance_attrs(
-     append_when_useful(cib_nodes, node_el)
- 
- 
-+def get_node_names(cib: _Element) -> Set[str]:
-+    return {
-+        str(node.attrib["uname"])
-+        for node in get_nodes(get_root(cib)).iterfind("./node")
-+    }
-+
-+
- def _ensure_node_exists(tree, node_name, state_nodes=None):
-     """
-     Make sure node with specified name exists
-diff --git a/pcs/lib/commands/resource.py b/pcs/lib/commands/resource.py
-index d0e8f4db..82ce73e0 100644
---- a/pcs/lib/commands/resource.py
-+++ b/pcs/lib/commands/resource.py
-@@ -50,12 +50,16 @@ from pcs.lib.cib.tools import (
- from pcs.lib.env import LibraryEnvironment, WaitType
- from pcs.lib.errors import LibraryError
- from pcs.lib.external import CommandRunner
--from pcs.lib.node import get_existing_nodes_names_addrs
-+from pcs.lib.node import (
-+    get_existing_nodes_names_addrs,
-+    get_pacemaker_node_names,
-+)
- from pcs.lib.pacemaker import simulate as simulate_tools
- from pcs.lib.pacemaker.live import (
-     diff_cibs_xml,
-     get_cib,
-     get_cib_xml,
-+    get_cluster_status_dom,
-     has_resource_unmove_unban_expired_support,
-     push_cib_diff_xml,
-     resource_ban,
-@@ -1589,6 +1593,16 @@ def move(
-     )
- 
- 
-+def _nodes_exist_reports(
-+    cib: _Element, node_names: Iterable[str]
-+) -> ReportItemList:
-+    existing_node_names = get_pacemaker_node_names(cib)
-+    return [
-+        reports.ReportItem.error(reports.messages.NodeNotFound(node_name))
-+        for node_name in (set(node_names) - existing_node_names)
-+    ]
-+
-+
- def move_autoclean(
-     env: LibraryEnvironment,
-     resource_id: str,
-@@ -1626,6 +1640,9 @@ def move_autoclean(
-     if resource_el is not None:
-         report_list.extend(resource.common.validate_move(resource_el, master))
- 
-+    if node:
-+        report_list.extend(_nodes_exist_reports(cib, [node]))
-+
-     if env.report_processor.report_list(report_list).has_errors:
-         raise LibraryError()
- 
-@@ -1659,8 +1676,32 @@ def move_autoclean(
-     add_constraint_cib_diff = diff_cibs_xml(
-         env.cmd_runner(), env.report_processor, cib_xml, rsc_moved_cib_xml
-     )
-+    with get_tmp_cib(
-+        env.report_processor, rsc_moved_cib_xml
-+    ) as rsc_moved_constraint_cleared_cib_file:
-+        stdout, stderr, retval = resource_unmove_unban(
-+            env.cmd_runner(
-+                dict(CIB_file=rsc_moved_constraint_cleared_cib_file.name)
-+            ),
-+            resource_id,
-+            node,
-+            master,
-+        )
-+        if retval != 0:
-+            raise LibraryError(
-+                ReportItem.error(
-+                    reports.messages.ResourceUnmoveUnbanPcmkError(
-+                        resource_id, stdout, stderr
-+                    )
-+                )
-+            )
-+        rsc_moved_constraint_cleared_cib_file.seek(0)
-+        constraint_removed_cib = rsc_moved_constraint_cleared_cib_file.read()
-     remove_constraint_cib_diff = diff_cibs_xml(
--        env.cmd_runner(), env.report_processor, rsc_moved_cib_xml, cib_xml
-+        env.cmd_runner(),
-+        env.report_processor,
-+        rsc_moved_cib_xml,
-+        constraint_removed_cib,
-     )
- 
-     if not (add_constraint_cib_diff and remove_constraint_cib_diff):
-@@ -1689,13 +1730,15 @@ def move_autoclean(
-                     )
-                 )
-             )
--    _ensure_resource_is_not_moved(
-+    _ensure_resource_moved_and_not_moved_back(
-         env.cmd_runner,
-         env.report_processor,
-         etree_to_str(after_move_simulated_cib),
-         remove_constraint_cib_diff,
-         resource_id,
-         strict,
-+        resource_state_before,
-+        node,
-     )
-     push_cib_diff_xml(env.cmd_runner(), add_constraint_cib_diff)
-     env.report_processor.report(
-@@ -1704,13 +1747,15 @@ def move_autoclean(
-         )
-     )
-     env.wait_for_idle(wait_timeout)
--    _ensure_resource_is_not_moved(
-+    _ensure_resource_moved_and_not_moved_back(
-         env.cmd_runner,
-         env.report_processor,
-         get_cib_xml(env.cmd_runner()),
-         remove_constraint_cib_diff,
-         resource_id,
-         strict,
-+        resource_state_before,
-+        node,
-     )
-     push_cib_diff_xml(env.cmd_runner(), remove_constraint_cib_diff)
-     env.report_processor.report(
-@@ -1730,16 +1775,35 @@ def move_autoclean(
-         raise LibraryError()
- 
- 
--def _ensure_resource_is_not_moved(
-+def _ensure_resource_moved_and_not_moved_back(
-     runner_factory: Callable[[Optional[Mapping[str, str]]], CommandRunner],
-     report_processor: reports.ReportProcessor,
-     cib_xml: str,
-     remove_constraint_cib_diff: str,
-     resource_id: str,
-     strict: bool,
-+    resource_state_before: Dict[str, List[str]],
-+    node: Optional[str],
- ) -> None:
-     # pylint: disable=too-many-locals
-     with get_tmp_cib(report_processor, cib_xml) as rsc_unmove_cib_file:
-+        if not _was_resource_moved(
-+            node,
-+            resource_state_before,
-+            get_resource_state(
-+                get_cluster_status_dom(
-+                    runner_factory(dict(CIB_file=rsc_unmove_cib_file.name))
-+                ),
-+                resource_id,
-+            ),
-+        ):
-+            raise LibraryError(
-+                reports.ReportItem.error(
-+                    reports.messages.ResourceMoveNotAffectingResource(
-+                        resource_id
-+                    )
-+                )
-+            )
-         push_cib_diff_xml(
-             runner_factory(dict(CIB_file=rsc_unmove_cib_file.name)),
-             remove_constraint_cib_diff,
-@@ -1809,20 +1873,31 @@ def _resource_running_on_nodes(
-     return frozenset()
- 
- 
-+def _was_resource_moved(
-+    node: Optional[str],
-+    resource_state_before: Dict[str, List[str]],
-+    resource_state_after: Dict[str, List[str]],
-+) -> bool:
-+    running_on_nodes = _resource_running_on_nodes(resource_state_after)
-+    return not bool(
-+        resource_state_before
-+        and (  # running resource moved
-+            not running_on_nodes
-+            or (node and node not in running_on_nodes)
-+            or (resource_state_before == resource_state_after)
-+        )
-+    )
-+
-+
- def _move_wait_report(
-     resource_id: str,
-     node: Optional[str],
-     resource_state_before: Dict[str, List[str]],
-     resource_state_after: Dict[str, List[str]],
- ) -> ReportItem:
--    allowed_nodes = frozenset([node] if node else [])
--    running_on_nodes = _resource_running_on_nodes(resource_state_after)
--
-     severity = reports.item.ReportItemSeverity.info()
--    if resource_state_before and (  # running resource moved
--        not running_on_nodes
--        or (allowed_nodes and allowed_nodes.isdisjoint(running_on_nodes))
--        or (resource_state_before == resource_state_after)
-+    if not _was_resource_moved(
-+        node, resource_state_before, resource_state_after
-     ):
-         severity = reports.item.ReportItemSeverity.error()
-     if not resource_state_after:
-@@ -1873,14 +1948,18 @@ class _MoveBanTemplate:
-         lifetime=None,
-         wait: WaitType = False,
-     ):
-+        # pylint: disable=too-many-locals
-         # validate
-         wait_timeout = env.ensure_wait_satisfiable(wait)  # raises on error
- 
-+        cib = env.get_cib()
-         resource_el, report_list = resource.common.find_one_resource(
--            get_resources(env.get_cib()), resource_id
-+            get_resources(cib), resource_id
-         )
-         if resource_el is not None:
-             report_list.extend(self._validate(resource_el, master))
-+        if node:
-+            report_list.extend(_nodes_exist_reports(cib, [node]))
-         if env.report_processor.report_list(report_list).has_errors:
-             raise LibraryError()
- 
-diff --git a/pcs/lib/node.py b/pcs/lib/node.py
-index ff08f747..3a7f236e 100644
---- a/pcs/lib/node.py
-+++ b/pcs/lib/node.py
-@@ -3,6 +3,7 @@ from typing import (
-     List,
-     Optional,
-     Tuple,
-+    Set,
- )
- 
- from lxml.etree import _Element
-@@ -11,7 +12,7 @@ from pcs.common import reports
- from pcs.common.reports import ReportItemList
- from pcs.common.reports import ReportItemSeverity
- from pcs.common.reports.item import ReportItem
--from pcs.lib.cib.node import PacemakerNode
-+from pcs.lib.cib.node import PacemakerNode, get_node_names
- from pcs.lib.cib.resource import remote_node, guest_node
- from pcs.lib.corosync.config_facade import ConfigFacade as CorosyncConfigFacade
- from pcs.lib.corosync.node import CorosyncNode
-@@ -28,6 +29,10 @@ def get_existing_nodes_names(
-     )
- 
- 
-+def get_pacemaker_node_names(cib: _Element) -> Set[str]:
-+    return get_node_names(cib) | set(get_existing_nodes_names(None, cib)[0])
-+
-+
- def get_existing_nodes_names_addrs(
-     corosync_conf=None, cib=None, error_on_missing_name=False
- ):
-diff --git a/pcs_test/tier0/common/reports/test_messages.py b/pcs_test/tier0/common/reports/test_messages.py
-index c85aaa9c..4a7b4945 100644
---- a/pcs_test/tier0/common/reports/test_messages.py
-+++ b/pcs_test/tier0/common/reports/test_messages.py
-@@ -4515,6 +4515,18 @@ class ResourceMoveConstraintRemoved(NameBuildTest):
-         )
- 
- 
-+class ResourceMoveNotAffectingResource(NameBuildTest):
-+    def test_success(self):
-+        self.assert_message_from_report(
-+            (
-+                "Unable to move resource 'R1' using a location constraint. "
-+                "Current location of the resource may be affected by some "
-+                "other constraint."
-+            ),
-+            reports.ResourceMoveNotAffectingResource("R1"),
-+        )
-+
-+
- class ResourceMoveAffectsOtherResources(NameBuildTest):
-     def test_multiple(self):
-         self.assert_message_from_report(
-diff --git a/pcs_test/tier0/lib/commands/resource/test_resource_move_autoclean.py b/pcs_test/tier0/lib/commands/resource/test_resource_move_autoclean.py
-index 32d758de..1bd4ee82 100644
---- a/pcs_test/tier0/lib/commands/resource/test_resource_move_autoclean.py
-+++ b/pcs_test/tier0/lib/commands/resource/test_resource_move_autoclean.py
-@@ -20,6 +20,25 @@ from pcs_test.tools.command_env import get_env_tools
- from pcs_test.tools.misc import get_test_resource as rc
- 
- 
-+def _node_fixture(name, node_id):
-+    return f'<node id="{node_id}" uname="{name}"/>'
-+
-+
-+def _node_list_fixture(nodes):
-+    return "\n".join(
-+        _node_fixture(node_name, node_id)
-+        for node_id, node_name in enumerate(nodes)
-+    )
-+
-+
-+def _nodes_section_fixture(content):
-+    return f"""
-+    <nodes>
-+    {content}
-+    </nodes>
-+    """
-+
-+
- def _rsc_primitive_fixture(res_id):
-     return f'<primitive id="{res_id}"/>'
- 
-@@ -145,11 +164,17 @@ class MoveAutocleanSuccess(MoveAutocleanCommonSetup):
-             resources=_resources_tag(
-                 _resource_primitive + _resource_promotable_clone
-             ),
-+            nodes=_nodes_section_fixture(
-+                _node_list_fixture([self.orig_node, self.new_node])
-+            ),
-         )
-         self.orig_cib = etree_to_str(
-             xml_fromstring(self.config.calls.get(config_load_cib_name).stdout)
-         )
-         self.cib_with_constraint = '<updated_cib with_constraint="True"/>'
-+        self.cib_without_constraint = (
-+            '<cib with_constraint="False" updated="True"/>'
-+        )
-         self.cib_simulate_constraint = (
-             '<cib simulate="True" with_constraint="True"/>'
-         )
-@@ -160,6 +185,9 @@ class MoveAutocleanSuccess(MoveAutocleanCommonSetup):
-         self.cib_diff_add_constraint_updated_tmp_file_name = (
-             "cib_diff_add_constraint_updated"
-         )
-+        self.cib_constraint_removed_by_unmove_file_name = (
-+            "cib_constraint_removed_by_unmove"
-+        )
-         self.cib_diff_remove_constraint_orig_tmp_file_name = (
-             "cib_diff_remove_constraint_orig"
-         )
-@@ -220,13 +248,18 @@ class MoveAutocleanSuccess(MoveAutocleanCommonSetup):
-                 self.cib_diff_add_constraint_updated_tmp_file_name,
-                 orig_content=self.cib_with_constraint,
-             ),
-+            TmpFileCall(
-+                self.cib_constraint_removed_by_unmove_file_name,
-+                orig_content=self.cib_with_constraint,
-+                new_content=self.cib_without_constraint,
-+            ),
-             TmpFileCall(
-                 self.cib_diff_remove_constraint_orig_tmp_file_name,
-                 orig_content=self.cib_with_constraint,
-             ),
-             TmpFileCall(
-                 self.cib_diff_remove_constraint_updated_tmp_file_name,
--                orig_content=self.orig_cib,
-+                orig_content=self.cib_without_constraint,
-             ),
-             TmpFileCall(
-                 self.simulated_cib_add_constraint_tmp_file_name,
-@@ -296,6 +329,12 @@ class MoveAutocleanSuccess(MoveAutocleanCommonSetup):
-             stdout=self.cib_diff_add_constraint,
-             name="runner.cib.diff.add_constraint",
-         )
-+        self.config.runner.pcmk.resource_clear(
-+            resource=resource_id,
-+            master=is_promotable,
-+            node=self.new_node if with_node else None,
-+            env=dict(CIB_file=self.cib_constraint_removed_by_unmove_file_name),
-+        )
-         self.config.runner.cib.diff(
-             self.cib_diff_remove_constraint_orig_tmp_file_name,
-             self.cib_diff_remove_constraint_updated_tmp_file_name,
-@@ -308,6 +347,13 @@ class MoveAutocleanSuccess(MoveAutocleanCommonSetup):
-             cib_xml=self.cib_with_constraint,
-             name="pcmk.simulate.rsc.move",
-         )
-+        self.config.runner.pcmk.load_state(
-+            resources=status_after,
-+            name="runner.pcmk.load_state.mid_simulation",
-+            env=dict(
-+                CIB_file=self.cib_apply_diff_remove_constraint_from_simulated_cib_tmp_file_name
-+            ),
-+        )
-         self.config.runner.cib.push_diff(
-             cib_diff=self.cib_diff_remove_constraint,
-             name="pcmk.push_cib_diff.simulation.remove_constraint",
-@@ -335,6 +381,13 @@ class MoveAutocleanSuccess(MoveAutocleanCommonSetup):
-             self.cib_with_constraint,
-             name="load_cib_after_move",
-         )
-+        self.config.runner.pcmk.load_state(
-+            resources=status_after,
-+            name="runner.pcmk.load_state.after_push",
-+            env=dict(
-+                CIB_file=self.cib_apply_diff_remove_constraint_after_push_tmp_file_name
-+            ),
-+        )
-         self.config.runner.cib.push_diff(
-             cib_diff=self.cib_diff_remove_constraint,
-             name="pcmk.push_cib_diff.simulation.remove_constraint_after_move",
-@@ -380,6 +433,11 @@ class MoveAutocleanSuccess(MoveAutocleanCommonSetup):
-                 file_path=self.cib_diff_add_constraint_updated_tmp_file_name,
-                 content=self.cib_with_constraint,
-             ),
-+            fixture.debug(
-+                reports.codes.TMP_FILE_WRITE,
-+                file_path=self.cib_constraint_removed_by_unmove_file_name,
-+                content=self.cib_with_constraint,
-+            ),
-             fixture.debug(
-                 reports.codes.TMP_FILE_WRITE,
-                 file_path=self.cib_diff_remove_constraint_orig_tmp_file_name,
-@@ -388,7 +446,7 @@ class MoveAutocleanSuccess(MoveAutocleanCommonSetup):
-             fixture.debug(
-                 reports.codes.TMP_FILE_WRITE,
-                 file_path=self.cib_diff_remove_constraint_updated_tmp_file_name,
--                content=self.orig_cib,
-+                content=self.cib_without_constraint,
-             ),
-             fixture.debug(
-                 reports.codes.TMP_FILE_WRITE,
-@@ -758,9 +816,7 @@ class MoveAutocleanValidations(MoveAutocleanCommonSetup):
-             resources=_state_resource_fixture(resource_id, "Stopped"),
-         )
-         self.env_assist.assert_raise_library_error(
--            lambda: move_autoclean(
--                self.env_assist.get_env(), resource_id, node="node"
--            ),
-+            lambda: move_autoclean(self.env_assist.get_env(), resource_id),
-             [
-                 fixture.error(
-                     reports.codes.CANNOT_MOVE_RESOURCE_NOT_RUNNING,
-@@ -770,11 +826,33 @@ class MoveAutocleanValidations(MoveAutocleanCommonSetup):
-             expected_in_processor=False,
-         )
- 
-+    def test_node_not_found(self):
-+        resource_id = "A"
-+        node = "non_existing_node"
-+        self.config.runner.cib.load(
-+            resources=_resources_tag(_rsc_primitive_fixture(resource_id)),
-+        )
-+        self.env_assist.assert_raise_library_error(
-+            lambda: move_autoclean(
-+                self.env_assist.get_env(), resource_id, node
-+            ),
-+        )
-+        self.env_assist.assert_reports(
-+            [
-+                fixture.error(
-+                    reports.codes.NODE_NOT_FOUND,
-+                    node=node,
-+                    searched_types=[],
-+                )
-+            ],
-+        )
-+
-     def test_constraint_already_exist(self):
-         resource_id = "A"
-         config_load_cib_name = "load_cib"
-         node = "node1"
-         cib_with_constraint = '<cib with_constraint="True"/>'
-+        cib_without_constraint = '<cib with_constraint="False" updated="True"/>'
-         cib_rsc_move_tmp_file_name = "cib_rsc_move_tmp_file"
-         cib_diff_add_constraint_orig_tmp_file_name = (
-             "cib_diff_add_constraint_orig"
-@@ -788,6 +866,9 @@ class MoveAutocleanValidations(MoveAutocleanCommonSetup):
-         cib_diff_remove_constraint_updated_tmp_file_name = (
-             "cib_diff_remove_constraint_updated"
-         )
-+        cib_constraint_removed_by_unmove_file_name = (
-+            "cib_constraint_removed_by_unmove"
-+        )
-         self.config.runner.cib.load(
-             resources=_resources_tag(_rsc_primitive_fixture(resource_id)),
-             constraints=f"""
-@@ -795,6 +876,7 @@ class MoveAutocleanValidations(MoveAutocleanCommonSetup):
-                   <rsc_location id="prefer-{resource_id}" rsc="{resource_id}" role="Started" node="{node}" score="INFINITY"/>
-               </constraints>
-             """,
-+            nodes=_nodes_section_fixture(_node_list_fixture([node])),
-             name=config_load_cib_name,
-         )
-         orig_cib = etree_to_str(
-@@ -815,13 +897,18 @@ class MoveAutocleanValidations(MoveAutocleanCommonSetup):
-                     cib_diff_add_constraint_updated_tmp_file_name,
-                     orig_content=cib_with_constraint,
-                 ),
-+                TmpFileCall(
-+                    cib_constraint_removed_by_unmove_file_name,
-+                    orig_content=cib_with_constraint,
-+                    new_content=cib_without_constraint,
-+                ),
-                 TmpFileCall(
-                     cib_diff_remove_constraint_orig_tmp_file_name,
-                     orig_content=cib_with_constraint,
-                 ),
-                 TmpFileCall(
-                     cib_diff_remove_constraint_updated_tmp_file_name,
--                    orig_content=orig_cib,
-+                    orig_content=cib_without_constraint,
-                 ),
-             ]
-         )
-@@ -839,6 +926,11 @@ class MoveAutocleanValidations(MoveAutocleanCommonSetup):
-             stdout="",
-             name="runner.cib.diff.add_constraint",
-         )
-+        self.config.runner.pcmk.resource_clear(
-+            resource=resource_id,
-+            node=node,
-+            env=dict(CIB_file=cib_constraint_removed_by_unmove_file_name),
-+        )
-         self.config.runner.cib.diff(
-             cib_diff_remove_constraint_orig_tmp_file_name,
-             cib_diff_remove_constraint_updated_tmp_file_name,
-@@ -863,6 +955,11 @@ class MoveAutocleanValidations(MoveAutocleanCommonSetup):
-                     file_path=cib_diff_add_constraint_updated_tmp_file_name,
-                     content=cib_with_constraint,
-                 ),
-+                fixture.debug(
-+                    reports.codes.TMP_FILE_WRITE,
-+                    file_path=cib_constraint_removed_by_unmove_file_name,
-+                    content=cib_with_constraint,
-+                ),
-                 fixture.debug(
-                     reports.codes.TMP_FILE_WRITE,
-                     file_path=cib_diff_remove_constraint_orig_tmp_file_name,
-@@ -871,7 +968,7 @@ class MoveAutocleanValidations(MoveAutocleanCommonSetup):
-                 fixture.debug(
-                     reports.codes.TMP_FILE_WRITE,
-                     file_path=cib_diff_remove_constraint_updated_tmp_file_name,
--                    content=orig_cib,
-+                    content=cib_without_constraint,
-                 ),
-                 fixture.info(
-                     reports.codes.NO_ACTION_NECESSARY,
-@@ -896,6 +993,9 @@ class MoveAutocleanFailures(MoveAutocleanCommonSetup):
-         self.cib_diff_add_constraint = "diff_add_constraint"
-         self.cib_diff_remove_constraint = "diff_remove_constraint"
-         self.cib_with_constraint = '<cib with_constraint="True"/>'
-+        self.cib_without_constraint = (
-+            '<cib with_constraint="False" updated="True"/>'
-+        )
-         self.cib_rsc_move_tmp_file_name = "cib_rsc_move_tmp_file"
-         self.cib_diff_add_constraint_orig_tmp_file_name = (
-             "cib_diff_add_constraint_orig"
-@@ -903,6 +1003,9 @@ class MoveAutocleanFailures(MoveAutocleanCommonSetup):
-         self.cib_diff_add_constraint_updated_tmp_file_name = (
-             "cib_diff_add_constraint_updated"
-         )
-+        self.cib_constraint_removed_by_unmove_file_name = (
-+            "cib_constraint_removed_by_unmove"
-+        )
-         self.cib_diff_remove_constraint_orig_tmp_file_name = (
-             "cib_diff_remove_constraint_orig"
-         )
-@@ -951,6 +1054,9 @@ class MoveAutocleanFailures(MoveAutocleanCommonSetup):
- 
-         self.config.runner.cib.load(
-             resources=_resources_tag(_rsc_primitive_fixture(self.resource_id)),
-+            nodes=_nodes_section_fixture(
-+                _node_list_fixture(["node1", "node2"])
-+            ),
-             name=self.config_load_cib_name,
-         )
-         self.orig_cib = etree_to_str(
-@@ -979,13 +1085,18 @@ class MoveAutocleanFailures(MoveAutocleanCommonSetup):
-                 self.cib_diff_add_constraint_updated_tmp_file_name,
-                 orig_content=self.cib_with_constraint,
-             ),
-+            TmpFileCall(
-+                self.cib_constraint_removed_by_unmove_file_name,
-+                orig_content=self.cib_with_constraint,
-+                new_content=self.cib_without_constraint,
-+            ),
-             TmpFileCall(
-                 self.cib_diff_remove_constraint_orig_tmp_file_name,
-                 orig_content=self.cib_with_constraint,
-             ),
-             TmpFileCall(
-                 self.cib_diff_remove_constraint_updated_tmp_file_name,
--                orig_content=self.orig_cib,
-+                orig_content=self.cib_without_constraint,
-             ),
-             TmpFileCall(
-                 self.simulated_cib_add_constraint_tmp_file_name,
-@@ -1067,6 +1178,11 @@ class MoveAutocleanFailures(MoveAutocleanCommonSetup):
-             stdout=self.cib_diff_add_constraint,
-             name="runner.cib.diff.add_constraint",
-         )
-+        self.config.runner.pcmk.resource_clear(
-+            resource=self.resource_id,
-+            node=node,
-+            env=dict(CIB_file=self.cib_constraint_removed_by_unmove_file_name),
-+        )
-         self.config.runner.cib.diff(
-             self.cib_diff_remove_constraint_orig_tmp_file_name,
-             self.cib_diff_remove_constraint_updated_tmp_file_name,
-@@ -1081,6 +1197,15 @@ class MoveAutocleanFailures(MoveAutocleanCommonSetup):
-         )
-         if stage <= 1:
-             return
-+        self.config.runner.pcmk.load_state(
-+            resources=_state_resource_fixture(
-+                self.resource_id, "Started", node if node else "node2"
-+            ),
-+            name="runner.pcmk.load_state.mid_simulation",
-+            env=dict(
-+                CIB_file=self.cib_apply_diff_remove_constraint_from_simulated_cib_tmp_file_name
-+            ),
-+        )
-         self.config.runner.cib.push_diff(
-             cib_diff=self.cib_diff_remove_constraint,
-             name="pcmk.push_cib_diff.simulation.remove_constraint",
-@@ -1110,6 +1235,17 @@ class MoveAutocleanFailures(MoveAutocleanCommonSetup):
-             self.cib_with_constraint,
-             name="load_cib_after_move",
-         )
-+        if stage <= 3:
-+            return
-+        self.config.runner.pcmk.load_state(
-+            resources=_state_resource_fixture(
-+                self.resource_id, "Started", node if node else "node2"
-+            ),
-+            name="runner.pcmk.load_state.after_push",
-+            env=dict(
-+                CIB_file=self.cib_apply_diff_remove_constraint_after_push_tmp_file_name
-+            ),
-+        )
-         self.config.runner.cib.push_diff(
-             cib_diff=self.cib_diff_remove_constraint,
-             name="pcmk.push_cib_diff.simulation.remove_constraint_after_move",
-@@ -1126,7 +1262,7 @@ class MoveAutocleanFailures(MoveAutocleanCommonSetup):
-             ),
-             name="pcmk.simulate.rsc.unmove.after_push",
-         )
--        if stage <= 3:
-+        if stage <= 4:
-             return
-         self.config.runner.cib.push_diff(
-             cib_diff=self.cib_diff_remove_constraint,
-@@ -1153,6 +1289,11 @@ class MoveAutocleanFailures(MoveAutocleanCommonSetup):
-                 file_path=self.cib_diff_add_constraint_updated_tmp_file_name,
-                 content=self.cib_with_constraint,
-             ),
-+            fixture.debug(
-+                reports.codes.TMP_FILE_WRITE,
-+                file_path=self.cib_constraint_removed_by_unmove_file_name,
-+                content=self.cib_with_constraint,
-+            ),
-             fixture.debug(
-                 reports.codes.TMP_FILE_WRITE,
-                 file_path=self.cib_diff_remove_constraint_orig_tmp_file_name,
-@@ -1161,7 +1302,7 @@ class MoveAutocleanFailures(MoveAutocleanCommonSetup):
-             fixture.debug(
-                 reports.codes.TMP_FILE_WRITE,
-                 file_path=self.cib_diff_remove_constraint_updated_tmp_file_name,
--                content=self.orig_cib,
-+                content=self.cib_without_constraint,
-             ),
-             fixture.debug(
-                 reports.codes.TMP_FILE_WRITE,
-@@ -1199,7 +1340,7 @@ class MoveAutocleanFailures(MoveAutocleanCommonSetup):
-                 reports.codes.WAIT_FOR_IDLE_STARTED,
-                 timeout=0,
-             ),
--        ][: {None: None, 3: -2, 2: 7, 1: 5}[stage]]
-+        ][: {None: None, 4: -2, 3: 10, 2: 8, 1: 6}[stage]]
- 
-     def test_move_affects_other_resources_strict(self):
-         self.tmp_file_mock_obj.set_calls(
-@@ -1304,7 +1445,8 @@ class MoveAutocleanFailures(MoveAutocleanCommonSetup):
-                 ),
-             )
-         )
--        self.set_up_testing_env(stage=3)
-+        setup_stage = 4
-+        self.set_up_testing_env(stage=setup_stage)
-         self.env_assist.assert_raise_library_error(
-             lambda: move_autoclean(self.env_assist.get_env(), self.resource_id),
-             [
-@@ -1316,7 +1458,7 @@ class MoveAutocleanFailures(MoveAutocleanCommonSetup):
-             ],
-             expected_in_processor=False,
-         )
--        self.env_assist.assert_reports(self.get_reports(stage=3))
-+        self.env_assist.assert_reports(self.get_reports(stage=setup_stage))
- 
-     def test_unmove_after_push_affects_other_resources_strict(self):
-         self.tmp_file_mock_obj.set_calls(
-@@ -1330,7 +1472,8 @@ class MoveAutocleanFailures(MoveAutocleanCommonSetup):
-                 ),
-             )
-         )
--        self.set_up_testing_env(stage=3)
-+        setup_stage = 4
-+        self.set_up_testing_env(stage=setup_stage)
-         self.env_assist.assert_raise_library_error(
-             lambda: move_autoclean(
-                 self.env_assist.get_env(),
-@@ -1346,7 +1489,7 @@ class MoveAutocleanFailures(MoveAutocleanCommonSetup):
-             ],
-             expected_in_processor=False,
-         )
--        self.env_assist.assert_reports(self.get_reports(stage=3))
-+        self.env_assist.assert_reports(self.get_reports(stage=setup_stage))
- 
-     def test_resource_not_runnig_after_move(self):
-         self.tmp_file_mock_obj.set_calls(
-@@ -1381,8 +1524,113 @@ class MoveAutocleanFailures(MoveAutocleanCommonSetup):
-             ]
-         )
- 
-+    def test_simulation_resource_not_moved(self):
-+        node = "node2"
-+        different_node = f"different-{node}"
-+        setup_stage = 1
-+        self.tmp_file_mock_obj.set_calls(
-+            self.get_tmp_files_mocks(
-+                _simulation_transition_fixture(
-+                    _simulation_synapses_fixture(self.resource_id)
-+                ),
-+            )
-+            + [
-+                TmpFileCall(
-+                    self.cib_apply_diff_remove_constraint_from_simulated_cib_tmp_file_name,
-+                    orig_content=self.cib_simulate_constraint,
-+                ),
-+            ]
-+        )
-+        self.set_up_testing_env(node=node, stage=setup_stage)
-+        self.config.runner.pcmk.load_state(
-+            resources=_state_resource_fixture(
-+                self.resource_id, "Started", different_node
-+            ),
-+            name="runner.pcmk.load_state.final",
-+            env=dict(
-+                CIB_file=self.cib_apply_diff_remove_constraint_from_simulated_cib_tmp_file_name
-+            ),
-+        )
-+        self.env_assist.assert_raise_library_error(
-+            lambda: move_autoclean(
-+                self.env_assist.get_env(),
-+                self.resource_id,
-+                node=node,
-+            ),
-+            [
-+                fixture.error(
-+                    reports.codes.RESOURCE_MOVE_NOT_AFFECTING_RESOURCE,
-+                    resource_id=self.resource_id,
-+                )
-+            ],
-+            expected_in_processor=False,
-+        )
-+        self.env_assist.assert_reports(
-+            self.get_reports(stage=setup_stage)
-+            + [
-+                fixture.debug(
-+                    reports.codes.TMP_FILE_WRITE,
-+                    file_path=self.cib_apply_diff_remove_constraint_from_simulated_cib_tmp_file_name,
-+                    content=self.cib_simulate_constraint,
-+                ),
-+            ]
-+        )
-+
-+    def test_after_push_resource_not_moved(self):
-+        node = "node2"
-+        different_node = f"different-{node}"
-+        setup_stage = 3
-+        self.tmp_file_mock_obj.set_calls(
-+            self.get_tmp_files_mocks(
-+                _simulation_transition_fixture(
-+                    _simulation_synapses_fixture(self.resource_id)
-+                ),
-+                _simulation_transition_fixture(),
-+            )
-+            + [
-+                TmpFileCall(
-+                    self.cib_apply_diff_remove_constraint_after_push_tmp_file_name,
-+                    orig_content=self.cib_with_constraint,
-+                ),
-+            ]
-+        )
-+        self.set_up_testing_env(node=node, stage=setup_stage)
-+        self.config.runner.pcmk.load_state(
-+            resources=_state_resource_fixture(
-+                self.resource_id, "Started", different_node
-+            ),
-+            name="runner.pcmk.load_state.final",
-+            env=dict(
-+                CIB_file=self.cib_apply_diff_remove_constraint_after_push_tmp_file_name,
-+            ),
-+        )
-+        self.env_assist.assert_raise_library_error(
-+            lambda: move_autoclean(
-+                self.env_assist.get_env(),
-+                self.resource_id,
-+                node=node,
-+            ),
-+            [
-+                fixture.error(
-+                    reports.codes.RESOURCE_MOVE_NOT_AFFECTING_RESOURCE,
-+                    resource_id=self.resource_id,
-+                )
-+            ],
-+            expected_in_processor=False,
-+        )
-+        self.env_assist.assert_reports(
-+            self.get_reports(stage=setup_stage)
-+            + [
-+                fixture.debug(
-+                    reports.codes.TMP_FILE_WRITE,
-+                    file_path=self.cib_apply_diff_remove_constraint_after_push_tmp_file_name,
-+                    content=self.cib_with_constraint,
-+                ),
-+            ]
-+        )
-+
-     def test_resource_running_on_a_different_node(self):
--        node = "node1"
-+        node = "node2"
-         different_node = f"different-{node}"
-         self.tmp_file_mock_obj.set_calls(
-             self.get_tmp_files_mocks(
-diff --git a/pcs_test/tier0/lib/commands/resource/test_resource_move_ban.py b/pcs_test/tier0/lib/commands/resource/test_resource_move_ban.py
-index 5d57fa06..28dd1cd1 100644
---- a/pcs_test/tier0/lib/commands/resource/test_resource_move_ban.py
-+++ b/pcs_test/tier0/lib/commands/resource/test_resource_move_ban.py
-@@ -10,6 +10,29 @@ from pcs.common.reports import ReportItemSeverity as severities
- from pcs.common.reports import codes as report_codes
- from pcs.lib.commands import resource
- 
-+
-+def _node_fixture(name, node_id):
-+    return f'<node id="{node_id}" uname="{name}"/>'
-+
-+
-+def _node_list_fixture(nodes):
-+    return "\n".join(
-+        _node_fixture(node_name, node_id)
-+        for node_id, node_name in enumerate(nodes)
-+    )
-+
-+
-+def _nodes_section_fixture(content):
-+    return f"""
-+    <nodes>
-+    {content}
-+    </nodes>
-+    """
-+
-+
-+nodes_section = _nodes_section_fixture(
-+    _node_list_fixture(["node", "node1", "node2"])
-+)
- resources_primitive = """
-     <resources>
-         <primitive id="A" />
-@@ -128,8 +151,24 @@ class MoveBanBaseMixin(MoveBanClearBaseMixin):
-             expected_in_processor=False,
-         )
- 
-+    def test_node_not_found(self):
-+        self.config.runner.cib.load(resources=resources_primitive)
-+        node = "node"
-+        self.env_assist.assert_raise_library_error(
-+            lambda: self.lib_action(self.env_assist.get_env(), "A", node)
-+        )
-+        self.env_assist.assert_reports(
-+            [
-+                fixture.error(
-+                    report_codes.NODE_NOT_FOUND, node=node, searched_types=[]
-+                )
-+            ]
-+        )
-+
-     def test_all_options(self):
--        self.config.runner.cib.load(resources=resources_promotable)
-+        self.config.runner.cib.load(
-+            resources=resources_promotable, nodes=nodes_section
-+        )
-         self.config_pcmk_action(
-             resource="A-clone",
-             master=True,
-@@ -274,7 +313,9 @@ class MoveBanWaitMixin:
-     def setUp(self):
-         self.timeout = 10
-         self.env_assist, self.config = get_env_tools(self)
--        self.config.runner.cib.load(resources=resources_primitive)
-+        self.config.runner.cib.load(
-+            resources=resources_primitive, nodes=nodes_section
-+        )
- 
-     @mock.patch.object(
-         settings,
-diff --git a/pcs_test/tools/command_env/config_runner_pcmk.py b/pcs_test/tools/command_env/config_runner_pcmk.py
-index e276e03b..213941b8 100644
---- a/pcs_test/tools/command_env/config_runner_pcmk.py
-+++ b/pcs_test/tools/command_env/config_runner_pcmk.py
-@@ -706,6 +706,7 @@ class PcmkShortcuts:
-         stdout="",
-         stderr="",
-         returncode=0,
-+        env=None,
-     ):
-         """
-         Create a call for crm_resource --clear
-@@ -722,6 +723,7 @@ class PcmkShortcuts:
-         string stdout -- crm_resource's stdout
-         string stderr -- crm_resource's stderr
-         int returncode -- crm_resource's returncode
-+        dict env -- CommandRunner environment variables
-         """
-         # arguments are used via locals()
-         # pylint: disable=unused-argument
-diff --git a/pcs_test/tools/command_env/mock_runner.py b/pcs_test/tools/command_env/mock_runner.py
-index f7871fc2..8520ce02 100644
---- a/pcs_test/tools/command_env/mock_runner.py
-+++ b/pcs_test/tools/command_env/mock_runner.py
-@@ -143,6 +143,6 @@ class Runner:
-             env.update(env_extend)
-         if env != call.env:
-             raise self.__call_queue.error_with_context(
--                f"ENV doesn't match. Expected: {call.env}; Real: {env}"
-+                f"Command #{i}: ENV doesn't match. Expected: {call.env}; Real: {env}"
-             )
-         return call.stdout, call.stderr, call.returncode
-diff --git a/pcs_test/tools/fixture_cib.py b/pcs_test/tools/fixture_cib.py
-index 602491c8..bf02bacc 100644
---- a/pcs_test/tools/fixture_cib.py
-+++ b/pcs_test/tools/fixture_cib.py
-@@ -310,6 +310,7 @@ MODIFIER_GENERATORS = {
-     "replace": replace_all,
-     "append": append_all,
-     "resources": lambda xml: replace_all({"./configuration/resources": xml}),
-+    "nodes": lambda xml: replace_all({"./configuration/nodes": xml}),
-     "constraints": lambda xml: replace_all(
-         {"./configuration/constraints": xml}
-     ),
--- 
-2.31.1
-
diff --git a/SOURCES/bz2022463-01-fix-creating-empty-cib.patch b/SOURCES/bz2022463-01-fix-creating-empty-cib.patch
deleted file mode 100644
index 1437dd1..0000000
--- a/SOURCES/bz2022463-01-fix-creating-empty-cib.patch
+++ /dev/null
@@ -1,94 +0,0 @@
-From f0342f110bdb4a7421532b85ca0f49070c7e5c1e Mon Sep 17 00:00:00 2001
-From: Tomas Jelinek <tojeline@redhat.com>
-Date: Thu, 13 Jan 2022 17:32:38 +0100
-Subject: [PATCH 4/5] fix creating empty cib
-
----
- pcs/utils.py                | 21 +++++++++++----------
- pcs_test/tier1/test_misc.py | 25 ++++++++++++++++++++++++-
- 2 files changed, 35 insertions(+), 11 deletions(-)
-
-diff --git a/pcs/utils.py b/pcs/utils.py
-index ad2d4452..423ffc43 100644
---- a/pcs/utils.py
-+++ b/pcs/utils.py
-@@ -2067,16 +2067,17 @@ def write_empty_cib(cibfile):
-     """
-     Commandline options: no options
-     """
--    empty_xml = """<?xml version="1.0" encoding="UTF-8"?>
--<cib admin_epoch="0" epoch="1" num_updates="1" validate-with="pacemaker-1.2">
--  <configuration>
--    <crm_config/>
--    <nodes/>
--    <resources/>
--    <constraints/>
--  </configuration>
--  <status/>
--</cib>"""
-+    empty_xml = """
-+        <cib admin_epoch="0" epoch="1" num_updates="1" validate-with="pacemaker-3.1">
-+          <configuration>
-+            <crm_config/>
-+            <nodes/>
-+            <resources/>
-+            <constraints/>
-+          </configuration>
-+          <status/>
-+        </cib>
-+    """
-     with open(cibfile, "w") as f:
-         f.write(empty_xml)
- 
-diff --git a/pcs_test/tier1/test_misc.py b/pcs_test/tier1/test_misc.py
-index 29ca6a71..6e6f72fb 100644
---- a/pcs_test/tier1/test_misc.py
-+++ b/pcs_test/tier1/test_misc.py
-@@ -1,8 +1,10 @@
-+import os
- from unittest import TestCase
- 
- from pcs_test.tools.assertions import AssertPcsMixin
- from pcs_test.tools.misc import (
-     get_test_resource as rc,
-+    get_tmp_dir,
-     get_tmp_file,
-     outdent,
-     write_file_to_tmpfile,
-@@ -19,7 +21,7 @@ class ParseArgvDashDash(TestCase, AssertPcsMixin):
-     cmd = "constraint colocation add R1 with R2".split()
- 
-     def setUp(self):
--        self.temp_cib = get_tmp_file("tier1_misc")
-+        self.temp_cib = get_tmp_file("tier1_misc_dashdash")
-         write_file_to_tmpfile(rc("cib-empty.xml"), self.temp_cib)
-         self.pcs_runner = PcsRunner(self.temp_cib.name)
-         self.allowed_roles = format_list(const.PCMK_ROLES)
-@@ -89,3 +91,24 @@ class ParseArgvDashDash(TestCase, AssertPcsMixin):
-                 """
-             ),
-         )
-+
-+
-+class EmptyCibIsPcmk2Compatible(TestCase, AssertPcsMixin):
-+    # This test verifies that a default empty CIB created by pcs when -f points
-+    # to an empty file conforms to minimal schema version supported by
-+    # pacemaker 2.0. If pcs prints a message that CIB schema has been upgraded,
-+    # then the test fails and shows there is a bug. Bundle with promoted-max
-+    # requires CIB compliant with schema 3.1, which was introduced in pacemaker
-+    # 2.0.0.
-+    def setUp(self):
-+        self.cib_dir = get_tmp_dir("tier1_misc_empty_cib")
-+        self.pcs_runner = PcsRunner(os.path.join(self.cib_dir.name, "cib.xml"))
-+
-+    def tearDown(self):
-+        self.cib_dir.cleanup()
-+
-+    def test_success(self):
-+        self.assert_pcs_success(
-+            "resource bundle create b container docker image=my.img promoted-max=1".split(),
-+            "",
-+        )
--- 
-2.31.1
-
diff --git a/SOURCES/bz2028902-01-fix-enabling-corosync-qdevice.patch b/SOURCES/bz2028902-01-fix-enabling-corosync-qdevice.patch
deleted file mode 100644
index e45d0b9..0000000
--- a/SOURCES/bz2028902-01-fix-enabling-corosync-qdevice.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-From 6b4b0c0026e5077044e4e908d093cb613ae2e94e Mon Sep 17 00:00:00 2001
-From: Tomas Jelinek <tojeline@redhat.com>
-Date: Mon, 6 Dec 2021 16:06:31 +0100
-Subject: [PATCH 1/3] fix enabling corosync-qdevice
-
----
- pcsd/remote.rb | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/pcsd/remote.rb b/pcsd/remote.rb
-index c49db116..3574d665 100644
---- a/pcsd/remote.rb
-+++ b/pcsd/remote.rb
-@@ -2515,7 +2515,7 @@ def qdevice_client_enable(param, request, auth_user)
-   unless allowed_for_local_cluster(auth_user, Permissions::WRITE)
-     return 403, 'Permission denied'
-   end
--  if not ServiceChecker.new('corosync', enabled: true).is_enabled?('corosync')
-+  if not ServiceChecker.new(['corosync'], enabled: true).is_enabled?('corosync')
-     return pcsd_success('corosync is not enabled, skipping')
-   elsif enable_service('corosync-qdevice')
-     return pcsd_success('corosync-qdevice enabled')
--- 
-2.31.1
-
diff --git a/SOURCES/bz2032997-01-skip-checking-of-scsi-devices-to-be-removed.patch b/SOURCES/bz2032997-01-skip-checking-of-scsi-devices-to-be-removed.patch
deleted file mode 100644
index e11b09e..0000000
--- a/SOURCES/bz2032997-01-skip-checking-of-scsi-devices-to-be-removed.patch
+++ /dev/null
@@ -1,86 +0,0 @@
-From 082bded126151e4f4b4667a1d8337db741828da6 Mon Sep 17 00:00:00 2001
-From: Miroslav Lisik <mlisik@redhat.com>
-Date: Thu, 16 Dec 2021 14:12:58 +0100
-Subject: [PATCH 1/5] skip checking of scsi devices to be removed before
- unfencing to be added devices
-
----
- pcs/lib/commands/scsi.py                 |  3 ++-
- pcs_test/tier0/lib/commands/test_scsi.py | 21 +++++++++++++++++----
- 2 files changed, 19 insertions(+), 5 deletions(-)
-
-diff --git a/pcs/lib/commands/scsi.py b/pcs/lib/commands/scsi.py
-index ff20a563..ab732805 100644
---- a/pcs/lib/commands/scsi.py
-+++ b/pcs/lib/commands/scsi.py
-@@ -31,7 +31,8 @@ def unfence_node(
-         return
-     fence_scsi_bin = os.path.join(settings.fence_agent_binaries, "fence_scsi")
-     fenced_devices = []
--    for device in original_devices:
-+    # do not check devices being removed
-+    for device in sorted(set(original_devices) & set(updated_devices)):
-         stdout, stderr, return_code = env.cmd_runner().run(
-             [
-                 fence_scsi_bin,
-diff --git a/pcs_test/tier0/lib/commands/test_scsi.py b/pcs_test/tier0/lib/commands/test_scsi.py
-index 8ef9836a..bc2357a9 100644
---- a/pcs_test/tier0/lib/commands/test_scsi.py
-+++ b/pcs_test/tier0/lib/commands/test_scsi.py
-@@ -13,10 +13,13 @@ class TestUnfenceNode(TestCase):
-         self.old_devices = ["device1", "device3"]
-         self.new_devices = ["device3", "device0", "device2"]
-         self.added_devices = set(self.new_devices) - set(self.old_devices)
-+        self.check_devices = sorted(
-+            set(self.old_devices) & set(self.new_devices)
-+        )
-         self.node = "node1"
- 
-     def test_success_devices_to_unfence(self):
--        for old_dev in self.old_devices:
-+        for old_dev in self.check_devices:
-             self.config.runner.scsi.get_status(
-                 self.node, old_dev, name=f"runner.scsi.is_fenced.{old_dev}"
-             )
-@@ -38,9 +41,19 @@ class TestUnfenceNode(TestCase):
-         )
-         self.env_assist.assert_reports([])
- 
-+    def test_success_replace_unavailable_device(self):
-+        self.config.runner.scsi.unfence_node(self.node, {"device2"})
-+        scsi.unfence_node(
-+            self.env_assist.get_env(),
-+            self.node,
-+            {"device1"},
-+            {"device2"},
-+        )
-+        self.env_assist.assert_reports([])
-+
-     def test_unfencing_failure(self):
-         err_msg = "stderr"
--        for old_dev in self.old_devices:
-+        for old_dev in self.check_devices:
-             self.config.runner.scsi.get_status(
-                 self.node, old_dev, name=f"runner.scsi.is_fenced.{old_dev}"
-             )
-@@ -98,7 +111,7 @@ class TestUnfenceNode(TestCase):
- 
-     def test_unfencing_skipped_devices_are_fenced(self):
-         stdout_off = "Status: OFF"
--        for old_dev in self.old_devices:
-+        for old_dev in self.check_devices:
-             self.config.runner.scsi.get_status(
-                 self.node,
-                 old_dev,
-@@ -116,7 +129,7 @@ class TestUnfenceNode(TestCase):
-             [
-                 fixture.info(
-                     report_codes.STONITH_UNFENCING_SKIPPED_DEVICES_FENCED,
--                    devices=sorted(self.old_devices),
-+                    devices=sorted(self.check_devices),
-                 )
-             ]
-         )
--- 
-2.31.1
-
diff --git a/SOURCES/bz2036633-01-Make-ocf-linbit-drbd-agent-pass-OCF-validation.patch b/SOURCES/bz2036633-01-Make-ocf-linbit-drbd-agent-pass-OCF-validation.patch
deleted file mode 100644
index 455dcda..0000000
--- a/SOURCES/bz2036633-01-Make-ocf-linbit-drbd-agent-pass-OCF-validation.patch
+++ /dev/null
@@ -1,41 +0,0 @@
-From 46b079a93d1817f9c1d6a7403c70b30f59d19c20 Mon Sep 17 00:00:00 2001
-From: Tomas Jelinek <tojeline@redhat.com>
-Date: Tue, 4 Jan 2022 12:56:56 +0100
-Subject: [PATCH 2/5] Make ocf:linbit:drbd agent pass OCF validation
-
----
- data/ocf-1.0.rng | 18 ++++++++----------
- 1 file changed, 8 insertions(+), 10 deletions(-)
-
-diff --git a/data/ocf-1.0.rng b/data/ocf-1.0.rng
-index 36ba4611..1e14a83b 100644
---- a/data/ocf-1.0.rng
-+++ b/data/ocf-1.0.rng
-@@ -169,16 +169,14 @@ RNGs. Thank you.
-       <optional>
-         <element name="content">
-           <choice>
--            <attribute name="type">
--              <choice>
--                <value>boolean</value>
--                <value>string</value>
--                <value>integer</value>
--                <value>second</value><!-- used by fence agents -->
--                <value>int</value><!-- used by fence agents intead of integer -->
--                <value>time</value><!-- used by pacemaker metadata -->
--              </choice>
--            </attribute>
-+            <!--
-+              OCF 1.0 allows values: boolean, integer, string. Agents, however,
-+              quite often use other values: int (fence agents), numeric
-+              (ocf:linbit:drbd), second (fence agents), time (pacemaker
-+              metadata). Since pcs doesn't actually care about the type, we
-+              allow any type to keep compatibility with existing agents.
-+            -->
-+            <attribute name="type" />
-             <group>
-               <!--
-                 used by fence agents and processed by pcs even though it is not
--- 
-2.31.1
-
diff --git a/SOURCES/bz2044409-01-fix-backend-parameter-all-in-cluster-destroy.patch b/SOURCES/bz2044409-01-fix-backend-parameter-all-in-cluster-destroy.patch
deleted file mode 100644
index 1131d7f..0000000
--- a/SOURCES/bz2044409-01-fix-backend-parameter-all-in-cluster-destroy.patch
+++ /dev/null
@@ -1,23 +0,0 @@
-From fa75f40361bc39cbd645b8014713e4c0ad0cda18 Mon Sep 17 00:00:00 2001
-From: Ivan Devat <idevat@redhat.com>
-Date: Mon, 24 Jan 2022 14:08:54 +0100
-Subject: [PATCH 2/2] fix backend parameter "all" in cluster destroy
-
----
- src/app/backend/calls/destroyCluster.ts | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/app/backend/calls/destroyCluster.ts b/src/app/backend/calls/destroyCluster.ts
-index b6e83a41..cf41ea42 100644
---- a/src/app/backend/calls/destroyCluster.ts
-+++ b/src/app/backend/calls/destroyCluster.ts
-@@ -4,5 +4,5 @@ const { url } = endpoints.destroyCluster;
- 
- export const destroyCluster = (clusterName: string): CallResult =>
-   http.post(url({ clusterName }), {
--    params: [["--all", "1"]],
-+    params: [["all", "1"]],
-   });
--- 
-2.31.1
-
diff --git a/SOURCES/bz2047983-01-Fix-snmp-client.patch b/SOURCES/bz2047983-01-Fix-snmp-client.patch
deleted file mode 100644
index a520771..0000000
--- a/SOURCES/bz2047983-01-Fix-snmp-client.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-From 68aa09a89804084e2764b06f0ae37f56cc609bda Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Valentin=20Vidi=C4=87?= <vvidic@valentin-vidic.from.hr>
-Date: Wed, 15 Dec 2021 20:32:26 +0100
-Subject: [PATCH 1/2] Fix snmp client
-
-Required constant is missing causing the command to fail on startup and breaking the pcs_snmp_agent service.
----
- pcsd/pcsd-cli-main.rb | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/pcsd/pcsd-cli-main.rb b/pcsd/pcsd-cli-main.rb
-index 29b9006d..be72d543 100644
---- a/pcsd/pcsd-cli-main.rb
-+++ b/pcsd/pcsd-cli-main.rb
-@@ -10,6 +10,7 @@ require 'remote.rb'
- 
- 
- PCS = get_pcs_path()
-+PCS_INTERNAL = get_pcs_internal_path()
- $logger_device = StringIO.new
- $logger = Logger.new($logger_device)
- early_log($logger)
--- 
-2.34.1
-
diff --git a/SOURCES/bz2050274-01-process-invalid-OCF-agents-the-same-way-as-before.patch b/SOURCES/bz2050274-01-process-invalid-OCF-agents-the-same-way-as-before.patch
deleted file mode 100644
index 43a3d38..0000000
--- a/SOURCES/bz2050274-01-process-invalid-OCF-agents-the-same-way-as-before.patch
+++ /dev/null
@@ -1,934 +0,0 @@
-From ae3435418f0af6e5f22f463871aa90a5c5b2d15f Mon Sep 17 00:00:00 2001
-From: Tomas Jelinek <tojeline@redhat.com>
-Date: Fri, 4 Feb 2022 16:23:18 +0100
-Subject: [PATCH 1/3] process invalid OCF agents as if they complied with OCF
- 1.0
-
----
- pcs/common/reports/codes.py                   |   4 +-
- pcs/common/reports/messages.py                |  13 +-
- pcs/lib/commands/resource.py                  |   3 +-
- pcs/lib/commands/resource_agent.py            |   4 +-
- pcs/lib/commands/stonith.py                   |   3 +-
- pcs/lib/resource_agent/__init__.py            |   1 -
- pcs/lib/resource_agent/error.py               |  14 --
- pcs/lib/resource_agent/facade.py              |  37 ++-
- pcs/lib/resource_agent/xml.py                 |  15 +-
- .../tier0/common/reports/test_messages.py     |  18 +-
- .../tier0/lib/resource_agent/test_facade.py   |  47 ++++
- pcs_test/tier0/lib/resource_agent/test_xml.py | 226 ++++++++----------
- 12 files changed, 201 insertions(+), 184 deletions(-)
-
-diff --git a/pcs/common/reports/codes.py b/pcs/common/reports/codes.py
-index 3e0512d9..e8dee00f 100644
---- a/pcs/common/reports/codes.py
-+++ b/pcs/common/reports/codes.py
-@@ -36,8 +36,8 @@ ADD_REMOVE_CANNOT_SPECIFY_ADJACENT_ITEM_WITHOUT_ITEMS_TO_ADD = M(
-     "ADD_REMOVE_CANNOT_SPECIFY_ADJACENT_ITEM_WITHOUT_ITEMS_TO_ADD"
- )
- AGENT_GENERIC_ERROR = M("AGENT_GENERIC_ERROR")
--AGENT_IMPLEMENTS_UNSUPPORTED_OCF_VERSION = M(
--    "AGENT_IMPLEMENTS_UNSUPPORTED_OCF_VERSION"
-+AGENT_IMPLEMENTS_UNSUPPORTED_OCF_VERSION_ASSUMED_VERSION = M(
-+    "AGENT_IMPLEMENTS_UNSUPPORTED_OCF_VERSION_ASSUMED_VERSION"
- )
- AGENT_NAME_GUESS_FOUND_MORE_THAN_ONE = M("AGENT_NAME_GUESS_FOUND_MORE_THAN_ONE")
- AGENT_NAME_GUESS_FOUND_NONE = M("AGENT_NAME_GUESS_FOUND_NONE")
-diff --git a/pcs/common/reports/messages.py b/pcs/common/reports/messages.py
-index 9d665e73..7df1e1eb 100644
---- a/pcs/common/reports/messages.py
-+++ b/pcs/common/reports/messages.py
-@@ -3789,9 +3789,9 @@ class AgentNameGuessFoundNone(ReportItemMessage):
- 
- 
- @dataclass(frozen=True)
--class AgentImplementsUnsupportedOcfVersion(ReportItemMessage):
-+class AgentImplementsUnsupportedOcfVersionAssumedVersion(ReportItemMessage):
-     """
--    Specified agent implements OCF version not supported by pcs
-+    Specified agent implements OCF version not supported by pcs, assumed OCF 1.0
- 
-     agent -- name of the agent
-     ocf_version -- OCF version implemented by the agent
-@@ -3801,7 +3801,8 @@ class AgentImplementsUnsupportedOcfVersion(ReportItemMessage):
-     agent: str
-     ocf_version: str
-     supported_versions: List[str]
--    _code = codes.AGENT_IMPLEMENTS_UNSUPPORTED_OCF_VERSION
-+    assumed_version: str
-+    _code = codes.AGENT_IMPLEMENTS_UNSUPPORTED_OCF_VERSION_ASSUMED_VERSION
- 
-     @property
-     def message(self) -> str:
-@@ -3809,9 +3810,9 @@ class AgentImplementsUnsupportedOcfVersion(ReportItemMessage):
-         _is = format_plural(self.supported_versions, "is")
-         _version_list = format_list(self.supported_versions)
-         return (
--            f"Unable to process agent '{self.agent}' as it implements "
--            f"unsupported OCF version '{self.ocf_version}', supported "
--            f"{_version} {_is}: {_version_list}"
-+            f"Agent '{self.agent}' implements unsupported OCF version "
-+            f"'{self.ocf_version}', supported {_version} {_is}: "
-+            f"{_version_list}; assumed version '{self.assumed_version}'"
-         )
- 
- 
-diff --git a/pcs/lib/commands/resource.py b/pcs/lib/commands/resource.py
-index 82ce73e0..c4b6252c 100644
---- a/pcs/lib/commands/resource.py
-+++ b/pcs/lib/commands/resource.py
-@@ -84,7 +84,6 @@ from pcs.lib.resource_agent import (
-     ResourceAgentName,
-     split_resource_agent_name,
-     UnableToGetAgentMetadata,
--    UnsupportedOcfVersion,
- )
- from pcs.lib.tools import get_tmp_cib
- from pcs.lib.validate import ValueTimeInterval
-@@ -162,7 +161,7 @@ def _get_agent_facade(
-             else find_one_resource_agent_by_type(runner, report_processor, name)
-         )
-         return factory.facade_from_parsed_name(split_name)
--    except (UnableToGetAgentMetadata, UnsupportedOcfVersion) as e:
-+    except UnableToGetAgentMetadata as e:
-         if allow_absent_agent:
-             report_processor.report(
-                 resource_agent_error_to_report_item(
-diff --git a/pcs/lib/commands/resource_agent.py b/pcs/lib/commands/resource_agent.py
-index e6167b13..4a1831c0 100644
---- a/pcs/lib/commands/resource_agent.py
-+++ b/pcs/lib/commands/resource_agent.py
-@@ -139,7 +139,9 @@ def _complete_agent_list(
-         try:
-             split_name = split_resource_agent_name(name)
-             metadata = (
--                agent_factory.facade_from_parsed_name(split_name).metadata
-+                agent_factory.facade_from_parsed_name(
-+                    split_name, report_warnings=False
-+                ).metadata
-                 if describe
-                 else name_to_void_metadata(split_name)
-             )
-diff --git a/pcs/lib/commands/stonith.py b/pcs/lib/commands/stonith.py
-index 093f5be9..2aa299d7 100644
---- a/pcs/lib/commands/stonith.py
-+++ b/pcs/lib/commands/stonith.py
-@@ -45,7 +45,6 @@ from pcs.lib.resource_agent import (
-     ResourceAgentFacadeFactory,
-     ResourceAgentName,
-     UnableToGetAgentMetadata,
--    UnsupportedOcfVersion,
- )
- from pcs.lib.validate import validate_add_remove_items
- from pcs.lib.xml_tools import get_root
-@@ -62,7 +61,7 @@ def _get_agent_facade(
-             raise InvalidResourceAgentName(name)
-         full_name = ResourceAgentName("stonith", None, name)
-         return factory.facade_from_parsed_name(full_name)
--    except (UnableToGetAgentMetadata, UnsupportedOcfVersion) as e:
-+    except UnableToGetAgentMetadata as e:
-         if allow_absent_agent:
-             report_processor.report(
-                 resource_agent_error_to_report_item(
-diff --git a/pcs/lib/resource_agent/__init__.py b/pcs/lib/resource_agent/__init__.py
-index 4548017f..c6086331 100644
---- a/pcs/lib/resource_agent/__init__.py
-+++ b/pcs/lib/resource_agent/__init__.py
-@@ -10,7 +10,6 @@ from .error import (
-     ResourceAgentError,
-     resource_agent_error_to_report_item,
-     UnableToGetAgentMetadata,
--    UnsupportedOcfVersion,
- )
- from .facade import ResourceAgentFacade, ResourceAgentFacadeFactory
- from .list import (
-diff --git a/pcs/lib/resource_agent/error.py b/pcs/lib/resource_agent/error.py
-index d4178333..f1dd7f3d 100644
---- a/pcs/lib/resource_agent/error.py
-+++ b/pcs/lib/resource_agent/error.py
-@@ -2,8 +2,6 @@ from typing import Iterable
- 
- from pcs.common import reports
- 
--from . import const
--
- 
- class ResourceAgentError(Exception):
-     def __init__(self, agent_name: str):
-@@ -37,12 +35,6 @@ class UnableToGetAgentMetadata(ResourceAgentError):
-         self.message = message
- 
- 
--class UnsupportedOcfVersion(ResourceAgentError):
--    def __init__(self, agent_name: str, ocf_version: str):
--        super().__init__(agent_name)
--        self.ocf_version = ocf_version
--
--
- def resource_agent_error_to_report_item(
-     e: ResourceAgentError,
-     severity: reports.ReportItemSeverity = reports.ReportItemSeverity.error(),
-@@ -69,10 +61,4 @@ def resource_agent_error_to_report_item(
-         message = reports.messages.UnableToGetAgentMetadata(
-             e.agent_name, e.message
-         )
--    elif isinstance(e, UnsupportedOcfVersion):
--        message = reports.messages.AgentImplementsUnsupportedOcfVersion(
--            e.agent_name,
--            e.ocf_version,
--            sorted(const.SUPPORTED_OCF_VERSIONS),
--        )
-     return reports.ReportItem(severity, message)
-diff --git a/pcs/lib/resource_agent/facade.py b/pcs/lib/resource_agent/facade.py
-index 4dbb59b8..dea59a1a 100644
---- a/pcs/lib/resource_agent/facade.py
-+++ b/pcs/lib/resource_agent/facade.py
-@@ -188,18 +188,32 @@ class ResourceAgentFacadeFactory:
-         self._fenced_metadata = None
- 
-     def facade_from_parsed_name(
--        self, name: ResourceAgentName
-+        self, name: ResourceAgentName, report_warnings=True
-     ) -> ResourceAgentFacade:
-         """
-         Create ResourceAgentFacade based on specified agent name
- 
-         name -- agent name to get a facade for
-         """
--        return self._facade_from_metadata(
--            ocf_version_to_ocf_unified(
--                parse_metadata(name, load_metadata(self._runner, name))
--            )
-+        metadata, raw_ocf_version = parse_metadata(
-+            name,
-+            load_metadata(self._runner, name),
-         )
-+        if (
-+            report_warnings
-+            and raw_ocf_version not in const.SUPPORTED_OCF_VERSIONS
-+        ):
-+            self._report_processor.report(
-+                reports.ReportItem.warning(
-+                    reports.messages.AgentImplementsUnsupportedOcfVersionAssumedVersion(
-+                        name.full_name,
-+                        raw_ocf_version,
-+                        sorted(const.SUPPORTED_OCF_VERSIONS),
-+                        const.OCF_1_0,
-+                    )
-+                )
-+            )
-+        return self._facade_from_metadata(ocf_version_to_ocf_unified(metadata))
- 
-     def void_facade_from_parsed_name(
-         self, name: ResourceAgentName
-@@ -232,15 +246,12 @@ class ResourceAgentFacadeFactory:
-                 const.FAKE_AGENT_STANDARD, None, const.PACEMAKER_FENCED
-             )
-             try:
-+                metadata, _ = parse_metadata(
-+                    agent_name,
-+                    load_fake_agent_metadata(self._runner, agent_name.type),
-+                )
-                 self._fenced_metadata = ocf_unified_to_pcs(
--                    ocf_version_to_ocf_unified(
--                        parse_metadata(
--                            agent_name,
--                            load_fake_agent_metadata(
--                                self._runner, agent_name.type
--                            ),
--                        )
--                    )
-+                    ocf_version_to_ocf_unified(metadata)
-                 )
-             except ResourceAgentError as e:
-                 # If pcs is unable to load fenced metadata, cache an empty
-diff --git a/pcs/lib/resource_agent/xml.py b/pcs/lib/resource_agent/xml.py
-index 82f8fbfa..1ba97216 100644
---- a/pcs/lib/resource_agent/xml.py
-+++ b/pcs/lib/resource_agent/xml.py
-@@ -8,7 +8,7 @@ from pcs.common.tools import xml_fromstring
- from pcs.lib.external import CommandRunner
- 
- from . import const
--from .error import UnableToGetAgentMetadata, UnsupportedOcfVersion
-+from .error import UnableToGetAgentMetadata
- from .types import (
-     FakeAgentName,
-     ResourceAgentActionOcf1_0,
-@@ -137,8 +137,11 @@ def load_fake_agent_metadata(
- 
- 
- def parse_metadata(
--    name: ResourceAgentName, metadata: _Element
--) -> Union[ResourceAgentMetadataOcf1_0, ResourceAgentMetadataOcf1_1]:
-+    name: ResourceAgentName,
-+    metadata: _Element,
-+) -> Tuple[
-+    Union[ResourceAgentMetadataOcf1_0, ResourceAgentMetadataOcf1_1], str
-+]:
-     """
-     Parse XML metadata to a dataclass
- 
-@@ -146,11 +149,9 @@ def parse_metadata(
-     metadata -- metadata XML document
-     """
-     ocf_version = _get_ocf_version(metadata)
--    if ocf_version == const.OCF_1_0:
--        return _parse_agent_1_0(name, metadata)
-     if ocf_version == const.OCF_1_1:
--        return _parse_agent_1_1(name, metadata)
--    raise UnsupportedOcfVersion(name.full_name, ocf_version)
-+        return _parse_agent_1_1(name, metadata), ocf_version
-+    return _parse_agent_1_0(name, metadata), ocf_version
- 
- 
- def _parse_agent_1_0(
-diff --git a/pcs_test/tier0/common/reports/test_messages.py b/pcs_test/tier0/common/reports/test_messages.py
-index 4a7b4945..b885a9eb 100644
---- a/pcs_test/tier0/common/reports/test_messages.py
-+++ b/pcs_test/tier0/common/reports/test_messages.py
-@@ -2833,22 +2833,22 @@ class AgentNameGuessFoundNone(NameBuildTest):
-         )
- 
- 
--class AgentImplementsUnsupportedOcfVersion(NameBuildTest):
-+class AgentImplementsUnsupportedOcfVersionAssumedVersion(NameBuildTest):
-     def test_singular(self):
-         self.assert_message_from_report(
--            "Unable to process agent 'agent-name' as it implements unsupported "
--            "OCF version 'ocf-2.3', supported version is: 'v1'",
--            reports.AgentImplementsUnsupportedOcfVersion(
--                "agent-name", "ocf-2.3", ["v1"]
-+            "Agent 'agent-name' implements unsupported OCF version 'ocf-2.3', "
-+            "supported version is: 'v1'; assumed version 'v1'",
-+            reports.AgentImplementsUnsupportedOcfVersionAssumedVersion(
-+                "agent-name", "ocf-2.3", ["v1"], "v1"
-             ),
-         )
- 
-     def test_plural(self):
-         self.assert_message_from_report(
--            "Unable to process agent 'agent-name' as it implements unsupported "
--            "OCF version 'ocf-2.3', supported versions are: 'v1', 'v2', 'v3'",
--            reports.AgentImplementsUnsupportedOcfVersion(
--                "agent-name", "ocf-2.3", ["v1", "v2", "v3"]
-+            "Agent 'agent-name' implements unsupported OCF version 'ocf-2.3', "
-+            "supported versions are: 'v1', 'v2', 'v3'; assumed version 'v1'",
-+            reports.AgentImplementsUnsupportedOcfVersionAssumedVersion(
-+                "agent-name", "ocf-2.3", ["v1", "v2", "v3"], "v1"
-             ),
-         )
- 
-diff --git a/pcs_test/tier0/lib/resource_agent/test_facade.py b/pcs_test/tier0/lib/resource_agent/test_facade.py
-index 654eb35e..f6a9899c 100644
---- a/pcs_test/tier0/lib/resource_agent/test_facade.py
-+++ b/pcs_test/tier0/lib/resource_agent/test_facade.py
-@@ -92,6 +92,14 @@ class ResourceAgentFacadeFactory(TestCase):
-             </parameters>
-         </resource-agent>
-     """
-+    _fixture_agent_bad_version_xml = """
-+        <resource-agent name="agent">
-+            <version>0.1.2</version>
-+            <parameters>
-+                <parameter name="agent-param"/>
-+            </parameters>
-+        </resource-agent>
-+    """
-     _fixture_fenced_xml = """
-         <resource-agent name="pacemaker-fenced">
-             <parameters>
-@@ -125,6 +133,45 @@ class ResourceAgentFacadeFactory(TestCase):
-         self.assertEqual(facade.metadata.name, name)
-         self.assertTrue(facade.metadata.agent_exists)
- 
-+    def test_facade_bad_ocf_version(self):
-+        name = ra.ResourceAgentName("service", None, "daemon")
-+        self.config.runner.pcmk.load_agent(
-+            agent_name="service:daemon",
-+            stdout=self._fixture_agent_bad_version_xml,
-+        )
-+
-+        env = self.env_assist.get_env()
-+        facade = ra.ResourceAgentFacadeFactory(
-+            env.cmd_runner(), env.report_processor
-+        ).facade_from_parsed_name(name)
-+        self.assertEqual(facade.metadata.name, name)
-+        self.assertTrue(facade.metadata.agent_exists)
-+        self.env_assist.assert_reports(
-+            [
-+                fixture.warn(
-+                    reports.codes.AGENT_IMPLEMENTS_UNSUPPORTED_OCF_VERSION_ASSUMED_VERSION,
-+                    agent=name.full_name,
-+                    ocf_version="0.1.2",
-+                    supported_versions=sorted(ra.const.SUPPORTED_OCF_VERSIONS),
-+                    assumed_version=ra.const.OCF_1_0,
-+                )
-+            ]
-+        )
-+
-+    def test_facade_bad_ocf_version_disabled_warning(self):
-+        name = ra.ResourceAgentName("service", None, "daemon")
-+        self.config.runner.pcmk.load_agent(
-+            agent_name="service:daemon",
-+            stdout=self._fixture_agent_bad_version_xml,
-+        )
-+
-+        env = self.env_assist.get_env()
-+        facade = ra.ResourceAgentFacadeFactory(
-+            env.cmd_runner(), env.report_processor
-+        ).facade_from_parsed_name(name, report_warnings=False)
-+        self.assertEqual(facade.metadata.name, name)
-+        self.assertTrue(facade.metadata.agent_exists)
-+
-     def test_facade_missing_agent(self):
-         name = ra.ResourceAgentName("service", None, "daemon")
-         self.config.runner.pcmk.load_agent(
-diff --git a/pcs_test/tier0/lib/resource_agent/test_xml.py b/pcs_test/tier0/lib/resource_agent/test_xml.py
-index c4176f32..26bbbb7d 100644
---- a/pcs_test/tier0/lib/resource_agent/test_xml.py
-+++ b/pcs_test/tier0/lib/resource_agent/test_xml.py
-@@ -351,6 +351,7 @@ class LoadFakeAgentMetadata(TestCase):
- class ParseOcfToolsMixin:
-     agent_name = ra.ResourceAgentName("ocf", "pacemaker", "Dummy")
-     ocf_version = None
-+    parsed_ocf_version = None
- 
-     def parse(self, xml, agent_name=None):
-         agent_name = agent_name or self.agent_name
-@@ -383,19 +384,17 @@ class ParseOcfToolsMixin:
-             version_el.text = ocf_version
-         return etree_to_str(dom)
- 
--
--class ParseOcfGeneric(ParseOcfToolsMixin, TestCase):
--    def test_unsupported_ocf_version(self):
--        with self.assertRaises(ra.UnsupportedOcfVersion) as cm:
--            self.parse(self.xml("""<resource-agent/>""", ocf_version="1.2"))
--        self.assertEqual(cm.exception.agent_name, self.agent_name.full_name)
--        self.assertEqual(cm.exception.ocf_version, "1.2")
-+    def assert_parse_result(self, xml, metadata):
-+        self.assertEqual(
-+            self.parse(xml),
-+            (metadata, self.parsed_ocf_version or self.ocf_version),
-+        )
- 
- 
- class ParseOcf10BaseMixin(ParseOcfToolsMixin):
-     def test_empty_agent(self):
--        self.assertEqual(
--            self.parse(self.xml("""<resource-agent/>""")),
-+        self.assert_parse_result(
-+            self.xml("""<resource-agent/>"""),
-             ResourceAgentMetadataOcf1_0(
-                 self.agent_name,
-                 shortdesc=None,
-@@ -406,16 +405,14 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
-         )
- 
-     def test_desc_element(self):
--        self.assertEqual(
--            self.parse(
--                self.xml(
--                    """
-+        self.assert_parse_result(
-+            self.xml(
-+                """
-                         <resource-agent>
-                             <shortdesc>This is a shortdesc</shortdesc>
-                             <longdesc>This is a longdesc</longdesc>
-                         </resource-agent>
-                     """
--                )
-             ),
-             ResourceAgentMetadataOcf1_0(
-                 self.agent_name,
-@@ -427,16 +424,14 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
-         )
- 
-     def test_desc_element_empty(self):
--        self.assertEqual(
--            self.parse(
--                self.xml(
--                    """
-+        self.assert_parse_result(
-+            self.xml(
-+                """
-                         <resource-agent>
-                             <longdesc/>
-                             <shortdesc/>
-                         </resource-agent>
-                     """
--                )
-             ),
-             ResourceAgentMetadataOcf1_0(
-                 self.agent_name,
-@@ -448,15 +443,13 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
-         )
- 
-     def test_desc_attribute(self):
--        self.assertEqual(
--            self.parse(
--                self.xml(
--                    """
-+        self.assert_parse_result(
-+            self.xml(
-+                """
-                         <resource-agent shortdesc="This is a shortdesc">
-                             <longdesc></longdesc>
-                         </resource-agent>
-                     """
--                )
-             ),
-             ResourceAgentMetadataOcf1_0(
-                 self.agent_name,
-@@ -468,13 +461,11 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
-         )
- 
-     def test_desc_attribute_empty(self):
--        self.assertEqual(
--            self.parse(
--                self.xml(
--                    """
-+        self.assert_parse_result(
-+            self.xml(
-+                """
-                         <resource-agent shortdesc=""/>
-                     """
--                )
-             ),
-             ResourceAgentMetadataOcf1_0(
-                 self.agent_name,
-@@ -486,15 +477,13 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
-         )
- 
-     def test_desc_element_and_attribute(self):
--        self.assertEqual(
--            self.parse(
--                self.xml(
--                    """
-+        self.assert_parse_result(
-+            self.xml(
-+                """
-                         <resource-agent shortdesc="shortdesc attribute">
-                             <shortdesc>shortdesc element</shortdesc>
-                         </resource-agent>
-                     """
--                )
-             ),
-             ResourceAgentMetadataOcf1_0(
-                 self.agent_name,
-@@ -506,15 +495,13 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
-         )
- 
-     def test_desc_element_empty_and_attribute(self):
--        self.assertEqual(
--            self.parse(
--                self.xml(
--                    """
-+        self.assert_parse_result(
-+            self.xml(
-+                """
-                         <resource-agent shortdesc="shortdesc attribute">
-                             <shortdesc></shortdesc>
-                         </resource-agent>
-                     """
--                )
-             ),
-             ResourceAgentMetadataOcf1_0(
-                 self.agent_name,
-@@ -526,15 +513,13 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
-         )
- 
-     def test_desc_element_empty_and_attribute_empty(self):
--        self.assertEqual(
--            self.parse(
--                self.xml(
--                    """
-+        self.assert_parse_result(
-+            self.xml(
-+                """
-                         <resource-agent shortdesc="">
-                             <shortdesc></shortdesc>
-                         </resource-agent>
-                     """
--                )
-             ),
-             ResourceAgentMetadataOcf1_0(
-                 self.agent_name,
-@@ -546,15 +531,13 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
-         )
- 
-     def test_parameters_empty_list(self):
--        self.assertEqual(
--            self.parse(
--                self.xml(
--                    """
-+        self.assert_parse_result(
-+            self.xml(
-+                """
-                         <resource-agent>
-                             <parameters/>
-                         </resource-agent>
-                     """
--                )
-             ),
-             ResourceAgentMetadataOcf1_0(
-                 self.agent_name,
-@@ -581,17 +564,15 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
-             )
- 
-     def test_parameters_minimal(self):
--        self.assertEqual(
--            self.parse(
--                self.xml(
--                    """
-+        self.assert_parse_result(
-+            self.xml(
-+                """
-                         <resource-agent>
-                             <parameters>
-                                 <parameter name="a_parameter"/>
-                             </parameters>
-                         </resource-agent>
-                     """
--                )
-             ),
-             ResourceAgentMetadataOcf1_0(
-                 self.agent_name,
-@@ -616,10 +597,9 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
-         )
- 
-     def test_parameters_all_settings(self):
--        self.assertEqual(
--            self.parse(
--                self.xml(
--                    """
-+        self.assert_parse_result(
-+            self.xml(
-+                """
-                         <resource-agent>
-                             <parameters>
-                                 <parameter name="a_parameter" required="1"
-@@ -632,7 +612,6 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
-                             </parameters>
-                         </resource-agent>
-                     """
--                )
-             ),
-             ResourceAgentMetadataOcf1_0(
-                 self.agent_name,
-@@ -657,10 +636,9 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
-         )
- 
-     def test_parameters_content(self):
--        self.assertEqual(
--            self.parse(
--                self.xml(
--                    """
-+        self.assert_parse_result(
-+            self.xml(
-+                """
-                         <resource-agent>
-                             <parameters>
-                                 <parameter name="with_type">
-@@ -676,7 +654,6 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
-                             </parameters>
-                         </resource-agent>
-                     """
--                )
-             ),
-             ResourceAgentMetadataOcf1_0(
-                 self.agent_name,
-@@ -713,15 +690,13 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
-         )
- 
-     def test_actions_empty_list(self):
--        self.assertEqual(
--            self.parse(
--                self.xml(
--                    """
-+        self.assert_parse_result(
-+            self.xml(
-+                """
-                         <resource-agent>
-                             <actions/>
-                         </resource-agent>
-                     """
--                )
-             ),
-             ResourceAgentMetadataOcf1_0(
-                 self.agent_name,
-@@ -748,10 +723,9 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
-             )
- 
-     def test_actions_multiple(self):
--        self.assertEqual(
--            self.parse(
--                self.xml(
--                    """
-+        self.assert_parse_result(
-+            self.xml(
-+                """
-                         <resource-agent>
-                             <actions>
-                                 <action name="minimal"/>
-@@ -764,7 +738,6 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
-                             </actions>
-                         </resource-agent>
-                     """
--                )
-             ),
-             ResourceAgentMetadataOcf1_0(
-                 self.agent_name,
-@@ -808,7 +781,26 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
- 
- 
- class ParseOcf10NoVersion(ParseOcf10BaseMixin, TestCase):
--    pass
-+    parsed_ocf_version = "1.0"
-+
-+
-+class ParseOcf10UnsupportedVersion(ParseOcf10BaseMixin, TestCase):
-+    ocf_version = "0.1.2"
-+
-+    # These tests test that pcs raises an error if an agent doesn't conform to
-+    # OCF schema. There is, however, no validation against OCF schema for
-+    # agents with unsupported OCF version. That means no error message, pcs
-+    # tries to process the agent and crashes. However bad that sounds, it's
-+    # indended as that's how pcs behaved before OCF 1.1 was implemented.
-+    # There's therefore no point in running these tests.
-+
-+    def test_parameters_empty_parameter(self):
-+        # parameters must have at least 'name' attribute
-+        pass
-+
-+    def test_actions_empty_action(self):
-+        # actions must have at least 'name' attribute
-+        pass
- 
- 
- class ParseOcf10ExplicitVersion(ParseOcf10BaseMixin, TestCase):
-@@ -819,8 +811,8 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
-     ocf_version = "1.1"
- 
-     def test_empty_agent(self):
--        self.assertEqual(
--            self.parse(self.xml("""<resource-agent/>""")),
-+        self.assert_parse_result(
-+            self.xml("""<resource-agent/>"""),
-             ResourceAgentMetadataOcf1_1(
-                 self.agent_name,
-                 shortdesc=None,
-@@ -831,16 +823,14 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
-         )
- 
-     def test_desc_element(self):
--        self.assertEqual(
--            self.parse(
--                self.xml(
--                    """
-+        self.assert_parse_result(
-+            self.xml(
-+                """
-                         <resource-agent>
-                             <shortdesc>This is a shortdesc</shortdesc>
-                             <longdesc>This is a longdesc</longdesc>
-                         </resource-agent>
-                     """
--                )
-             ),
-             ResourceAgentMetadataOcf1_1(
-                 self.agent_name,
-@@ -852,16 +842,14 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
-         )
- 
-     def test_desc_element_empty(self):
--        self.assertEqual(
--            self.parse(
--                self.xml(
--                    """
-+        self.assert_parse_result(
-+            self.xml(
-+                """
-                         <resource-agent>
-                             <longdesc/>
-                             <shortdesc/>
-                         </resource-agent>
-                     """
--                )
-             ),
-             ResourceAgentMetadataOcf1_1(
-                 self.agent_name,
-@@ -873,15 +861,13 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
-         )
- 
-     def test_parameters_empty_list(self):
--        self.assertEqual(
--            self.parse(
--                self.xml(
--                    """
-+        self.assert_parse_result(
-+            self.xml(
-+                """
-                         <resource-agent>
-                             <parameters/>
-                         </resource-agent>
-                     """
--                )
-             ),
-             ResourceAgentMetadataOcf1_1(
-                 self.agent_name,
-@@ -908,17 +894,15 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
-             )
- 
-     def test_parameters_minimal(self):
--        self.assertEqual(
--            self.parse(
--                self.xml(
--                    """
-+        self.assert_parse_result(
-+            self.xml(
-+                """
-                         <resource-agent>
-                             <parameters>
-                                 <parameter name="a_parameter"/>
-                             </parameters>
-                         </resource-agent>
-                     """
--                )
-             ),
-             ResourceAgentMetadataOcf1_1(
-                 self.agent_name,
-@@ -945,10 +929,9 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
-         )
- 
-     def test_parameters_deprecated_minimal(self):
--        self.assertEqual(
--            self.parse(
--                self.xml(
--                    """
-+        self.assert_parse_result(
-+            self.xml(
-+                """
-                         <resource-agent>
-                             <parameters>
-                                 <parameter name="a_parameter">
-@@ -957,7 +940,6 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
-                             </parameters>
-                         </resource-agent>
-                     """
--                )
-             ),
-             ResourceAgentMetadataOcf1_1(
-                 self.agent_name,
-@@ -984,10 +966,9 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
-         )
- 
-     def test_parameters_deprecated_replaced_with(self):
--        self.assertEqual(
--            self.parse(
--                self.xml(
--                    """
-+        self.assert_parse_result(
-+            self.xml(
-+                """
-                         <resource-agent>
-                             <parameters>
-                                 <parameter name="a_parameter">
-@@ -999,7 +980,6 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
-                             </parameters>
-                         </resource-agent>
-                     """
--                )
-             ),
-             ResourceAgentMetadataOcf1_1(
-                 self.agent_name,
-@@ -1026,10 +1006,9 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
-         )
- 
-     def test_parameters_all_settings(self):
--        self.assertEqual(
--            self.parse(
--                self.xml(
--                    """
-+        self.assert_parse_result(
-+            self.xml(
-+                """
-                         <resource-agent>
-                             <parameters>
-                                 <parameter name="a_parameter"
-@@ -1048,7 +1027,6 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
-                             </parameters>
-                         </resource-agent>
-                     """
--                )
-             ),
-             ResourceAgentMetadataOcf1_1(
-                 self.agent_name,
-@@ -1075,10 +1053,9 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
-         )
- 
-     def test_parameters_content(self):
--        self.assertEqual(
--            self.parse(
--                self.xml(
--                    """
-+        self.assert_parse_result(
-+            self.xml(
-+                """
-                         <resource-agent>
-                             <parameters>
-                                 <parameter name="with_type">
-@@ -1094,7 +1071,6 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
-                             </parameters>
-                         </resource-agent>
-                     """
--                )
-             ),
-             ResourceAgentMetadataOcf1_1(
-                 self.agent_name,
-@@ -1135,15 +1111,13 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
-         )
- 
-     def test_actions_empty_list(self):
--        self.assertEqual(
--            self.parse(
--                self.xml(
--                    """
-+        self.assert_parse_result(
-+            self.xml(
-+                """
-                         <resource-agent>
-                             <actions/>
-                         </resource-agent>
-                     """
--                )
-             ),
-             ResourceAgentMetadataOcf1_1(
-                 self.agent_name,
-@@ -1170,10 +1144,9 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
-             )
- 
-     def test_actions_multiple(self):
--        self.assertEqual(
--            self.parse(
--                self.xml(
--                    """
-+        self.assert_parse_result(
-+            self.xml(
-+                """
-                         <resource-agent>
-                             <actions>
-                                 <action name="minimal"/>
-@@ -1186,7 +1159,6 @@ class ParseOcf11(ParseOcfToolsMixin, TestCase):
-                             </actions>
-                         </resource-agent>
-                     """
--                )
-             ),
-             ResourceAgentMetadataOcf1_1(
-                 self.agent_name,
--- 
-2.34.1
-
diff --git a/SOURCES/bz2050274-02-relax-OCF-1.0-parser.patch b/SOURCES/bz2050274-02-relax-OCF-1.0-parser.patch
deleted file mode 100644
index 8e8c073..0000000
--- a/SOURCES/bz2050274-02-relax-OCF-1.0-parser.patch
+++ /dev/null
@@ -1,587 +0,0 @@
-From 65b30a04a234449cb4aa65606d47bf1d673592a4 Mon Sep 17 00:00:00 2001
-From: Tomas Jelinek <tojeline@redhat.com>
-Date: Wed, 9 Feb 2022 11:16:49 +0100
-Subject: [PATCH 2/3] relax OCF 1.0 parser
-
----
- pcs/lib/resource_agent/facade.py              |  50 ++++--
- pcs/lib/resource_agent/ocf_transform.py       |  51 +++++-
- pcs/lib/resource_agent/xml.py                 |   8 +-
- .../tier0/lib/resource_agent/test_facade.py   |  44 +++++
- .../lib/resource_agent/test_ocf_transform.py  |  48 +++++-
- pcs_test/tier0/lib/resource_agent/test_xml.py | 155 ++++++++++--------
- 6 files changed, 256 insertions(+), 100 deletions(-)
-
-diff --git a/pcs/lib/resource_agent/facade.py b/pcs/lib/resource_agent/facade.py
-index dea59a1a..8a65eb1c 100644
---- a/pcs/lib/resource_agent/facade.py
-+++ b/pcs/lib/resource_agent/facade.py
-@@ -2,12 +2,19 @@ from collections import defaultdict
- from dataclasses import replace as dc_replace
- from typing import Dict, Iterable, List, Optional, Set
- 
-+from lxml import etree
-+
-+from pcs import settings
- from pcs.common import reports
- from pcs.lib import validate
- from pcs.lib.external import CommandRunner
- 
- from . import const
--from .error import ResourceAgentError, resource_agent_error_to_report_item
-+from .error import (
-+    ResourceAgentError,
-+    resource_agent_error_to_report_item,
-+    UnableToGetAgentMetadata,
-+)
- from .name import name_to_void_metadata
- from .ocf_transform import ocf_version_to_ocf_unified
- from .pcs_transform import get_additional_trace_parameters, ocf_unified_to_pcs
-@@ -195,24 +202,33 @@ class ResourceAgentFacadeFactory:
- 
-         name -- agent name to get a facade for
-         """
--        metadata, raw_ocf_version = parse_metadata(
--            name,
--            load_metadata(self._runner, name),
--        )
--        if (
--            report_warnings
--            and raw_ocf_version not in const.SUPPORTED_OCF_VERSIONS
--        ):
--            self._report_processor.report(
--                reports.ReportItem.warning(
--                    reports.messages.AgentImplementsUnsupportedOcfVersionAssumedVersion(
--                        name.full_name,
--                        raw_ocf_version,
--                        sorted(const.SUPPORTED_OCF_VERSIONS),
--                        const.OCF_1_0,
-+        dom_metadata = load_metadata(self._runner, name)
-+        metadata, raw_ocf_version = parse_metadata(name, dom_metadata)
-+        if report_warnings:
-+            if raw_ocf_version not in const.SUPPORTED_OCF_VERSIONS:
-+                self._report_processor.report(
-+                    reports.ReportItem.warning(
-+                        reports.messages.AgentImplementsUnsupportedOcfVersionAssumedVersion(
-+                            name.full_name,
-+                            raw_ocf_version,
-+                            sorted(const.SUPPORTED_OCF_VERSIONS),
-+                            const.OCF_1_0,
-+                        )
-                     )
-                 )
--            )
-+            if raw_ocf_version != const.OCF_1_1:
-+                try:
-+                    etree.RelaxNG(
-+                        file=settings.path.ocf_1_0_schema
-+                    ).assertValid(dom_metadata)
-+                except etree.DocumentInvalid as e:
-+                    self._report_processor.report(
-+                        resource_agent_error_to_report_item(
-+                            UnableToGetAgentMetadata(name.full_name, str(e)),
-+                            severity=reports.ReportItemSeverity.warning(),
-+                            is_stonith=name.is_stonith,
-+                        )
-+                    )
-         return self._facade_from_metadata(ocf_version_to_ocf_unified(metadata))
- 
-     def void_facade_from_parsed_name(
-diff --git a/pcs/lib/resource_agent/ocf_transform.py b/pcs/lib/resource_agent/ocf_transform.py
-index e841b55e..7e6a14ad 100644
---- a/pcs/lib/resource_agent/ocf_transform.py
-+++ b/pcs/lib/resource_agent/ocf_transform.py
-@@ -67,20 +67,42 @@ def _ocf_1_1_to_ocf_unified(
-         longdesc=metadata.longdesc,
-         parameters=_ocf_1_1_parameter_list_to_ocf_unified(metadata.parameters),
-         # OCF 1.1 actions are the same as in OCF 1.0
--        actions=_ocf_1_0_action_list_to_ocf_unified(metadata.actions),
-+        actions=_ocf_1_1_action_list_to_ocf_unified(metadata.actions),
-     )
- 
- 
- def _ocf_1_0_action_list_to_ocf_unified(
--    action_list: Iterable[
--        Union[ResourceAgentActionOcf1_0, ResourceAgentActionOcf1_1]
--    ],
-+    action_list: Iterable[ResourceAgentActionOcf1_0],
- ) -> List[ResourceAgentAction]:
-     """
-     Transform OCF 1.0 actions to a universal format
- 
-     action_list -- actions according OCF 1.0
-     """
-+    return [
-+        ResourceAgentAction(
-+            name=action.name,
-+            timeout=action.timeout,
-+            interval=action.interval,
-+            role=action.role,
-+            start_delay=action.start_delay,
-+            depth=action.depth,
-+            automatic=_bool_value_legacy(action.automatic),
-+            on_target=_bool_value_legacy(action.on_target),
-+        )
-+        for action in action_list
-+        if action.name
-+    ]
-+
-+
-+def _ocf_1_1_action_list_to_ocf_unified(
-+    action_list: Iterable[ResourceAgentActionOcf1_1],
-+) -> List[ResourceAgentAction]:
-+    """
-+    Transform OCF 1.1 actions to a universal format
-+
-+    action_list -- actions according OCF 1.1
-+    """
-     return [
-         ResourceAgentAction(
-             name=action.name,
-@@ -111,6 +133,8 @@ def _ocf_1_0_parameter_list_to_ocf_unified(
- 
-     result = []
-     for parameter in parameter_list:
-+        if not parameter.name:
-+            continue
-         result.append(
-             ResourceAgentParameter(
-                 name=parameter.name,
-@@ -119,17 +143,17 @@ def _ocf_1_0_parameter_list_to_ocf_unified(
-                 type=parameter.type,
-                 default=parameter.default,
-                 enum_values=parameter.enum_values,
--                required=_bool_value(parameter.required),
-+                required=_bool_value_legacy(parameter.required),
-                 advanced=False,
--                deprecated=_bool_value(parameter.deprecated),
-+                deprecated=_bool_value_legacy(parameter.deprecated),
-                 deprecated_by=sorted(deprecated_by_dict[parameter.name]),
-                 deprecated_desc=None,
-                 unique_group=(
-                     f"{const.DEFAULT_UNIQUE_GROUP_PREFIX}{parameter.name}"
--                    if _bool_value(parameter.unique)
-+                    if _bool_value_legacy(parameter.unique)
-                     else None
-                 ),
--                reloadable=_bool_value(parameter.unique),
-+                reloadable=_bool_value_legacy(parameter.unique),
-             )
-         )
-     return result
-@@ -170,3 +194,14 @@ def _bool_value(value: Optional[str]) -> bool:
-     value -- raw bool value
-     """
-     return value == "1"
-+
-+
-+def _bool_value_legacy(value: Optional[str]) -> bool:
-+    """
-+    Transform raw bool value from metadata to bool type in backward compatible way
-+
-+    value -- raw bool value
-+    """
-+    return (
-+        False if not value else value.lower() in {"true", "on", "yes", "y", "1"}
-+    )
-diff --git a/pcs/lib/resource_agent/xml.py b/pcs/lib/resource_agent/xml.py
-index 1ba97216..0fc70527 100644
---- a/pcs/lib/resource_agent/xml.py
-+++ b/pcs/lib/resource_agent/xml.py
-@@ -94,9 +94,7 @@ def _metadata_xml_to_dom(metadata: str) -> _Element:
-     """
-     dom = xml_fromstring(metadata)
-     ocf_version = _get_ocf_version(dom)
--    if ocf_version == const.OCF_1_0:
--        etree.RelaxNG(file=settings.path.ocf_1_0_schema).assertValid(dom)
--    elif ocf_version == const.OCF_1_1:
-+    if ocf_version == const.OCF_1_1:
-         etree.RelaxNG(file=settings.path.ocf_1_1_schema).assertValid(dom)
-     return dom
- 
-@@ -230,7 +228,7 @@ def _parse_parameters_1_0(
-         )
-         result.append(
-             ResourceAgentParameterOcf1_0(
--                name=str(parameter_el.attrib["name"]),
-+                name=str(parameter_el.get("name", "")),
-                 shortdesc=_get_shortdesc(parameter_el),
-                 longdesc=_get_longdesc(parameter_el),
-                 type=value_type,
-@@ -286,7 +284,7 @@ def _parse_parameters_1_1(
- def _parse_actions_1_0(element: _Element) -> List[ResourceAgentActionOcf1_0]:
-     return [
-         ResourceAgentActionOcf1_0(
--            name=str(action.attrib["name"]),
-+            name=str(action.get("name", "")),
-             timeout=action.get("timeout"),
-             interval=action.get("interval"),
-             role=action.get("role"),
-diff --git a/pcs_test/tier0/lib/resource_agent/test_facade.py b/pcs_test/tier0/lib/resource_agent/test_facade.py
-index f6a9899c..313dfa2b 100644
---- a/pcs_test/tier0/lib/resource_agent/test_facade.py
-+++ b/pcs_test/tier0/lib/resource_agent/test_facade.py
-@@ -100,6 +100,13 @@ class ResourceAgentFacadeFactory(TestCase):
-             </parameters>
-         </resource-agent>
-     """
-+    _fixture_agent_not_valid_xml = """
-+        <resource-agent name="agent">
-+            <parameters>
-+                <parameter label="something wrong"/>
-+            </parameters>
-+        </resource-agent>
-+    """
-     _fixture_fenced_xml = """
-         <resource-agent name="pacemaker-fenced">
-             <parameters>
-@@ -172,6 +179,43 @@ class ResourceAgentFacadeFactory(TestCase):
-         self.assertEqual(facade.metadata.name, name)
-         self.assertTrue(facade.metadata.agent_exists)
- 
-+    def test_facade_ocf_1_0_not_valid(self):
-+        name = ra.ResourceAgentName("service", None, "daemon")
-+        self.config.runner.pcmk.load_agent(
-+            agent_name="service:daemon",
-+            stdout=self._fixture_agent_not_valid_xml,
-+        )
-+
-+        env = self.env_assist.get_env()
-+        facade = ra.ResourceAgentFacadeFactory(
-+            env.cmd_runner(), env.report_processor
-+        ).facade_from_parsed_name(name)
-+        self.assertEqual(facade.metadata.name, name)
-+        self.assertTrue(facade.metadata.agent_exists)
-+        self.env_assist.assert_reports(
-+            [
-+                fixture.warn(
-+                    reports.codes.UNABLE_TO_GET_AGENT_METADATA,
-+                    agent=name.full_name,
-+                    reason="Element parameter failed to validate attributes, line 3",
-+                )
-+            ]
-+        )
-+
-+    def test_facade_ocf_1_0_not_valid_disabled_warning(self):
-+        name = ra.ResourceAgentName("service", None, "daemon")
-+        self.config.runner.pcmk.load_agent(
-+            agent_name="service:daemon",
-+            stdout=self._fixture_agent_not_valid_xml,
-+        )
-+
-+        env = self.env_assist.get_env()
-+        facade = ra.ResourceAgentFacadeFactory(
-+            env.cmd_runner(), env.report_processor
-+        ).facade_from_parsed_name(name, report_warnings=False)
-+        self.assertEqual(facade.metadata.name, name)
-+        self.assertTrue(facade.metadata.agent_exists)
-+
-     def test_facade_missing_agent(self):
-         name = ra.ResourceAgentName("service", None, "daemon")
-         self.config.runner.pcmk.load_agent(
-diff --git a/pcs_test/tier0/lib/resource_agent/test_ocf_transform.py b/pcs_test/tier0/lib/resource_agent/test_ocf_transform.py
-index 9e41b6af..d0de86e5 100644
---- a/pcs_test/tier0/lib/resource_agent/test_ocf_transform.py
-+++ b/pcs_test/tier0/lib/resource_agent/test_ocf_transform.py
-@@ -66,6 +66,18 @@ class OcfVersionToOcfUnified(TestCase):
-                     obsoletes=None,
-                     unique=None,
-                 ),
-+                ra.types.ResourceAgentParameterOcf1_0(
-+                    name="",
-+                    shortdesc="Parameters with no name are ignored",
-+                    longdesc=None,
-+                    type="string",
-+                    default=None,
-+                    enum_values=None,
-+                    required=None,
-+                    deprecated=None,
-+                    obsoletes=None,
-+                    unique=None,
-+                ),
-                 ra.types.ResourceAgentParameterOcf1_0(
-                     name="param_2",
-                     shortdesc="param_2 shortdesc",
-@@ -109,10 +121,10 @@ class OcfVersionToOcfUnified(TestCase):
-                     type="string",
-                     default=None,
-                     enum_values=None,
--                    required="1",
--                    deprecated="1",
-+                    required="yeS",
-+                    deprecated="True",
-                     obsoletes="param_4",
--                    unique="1",
-+                    unique="on",
-                 ),
-                 ra.types.ResourceAgentParameterOcf1_0(
-                     name="param_6",
-@@ -138,6 +150,16 @@ class OcfVersionToOcfUnified(TestCase):
-                     automatic=None,
-                     on_target=None,
-                 ),
-+                ra.types.ResourceAgentActionOcf1_0(
-+                    name="",
-+                    timeout=None,
-+                    interval=None,
-+                    role=None,
-+                    start_delay=None,
-+                    depth=None,
-+                    automatic=None,
-+                    on_target=None,
-+                ),
-                 ra.types.ResourceAgentActionOcf1_0(
-                     name="action_2",
-                     timeout="12",
-@@ -158,6 +180,16 @@ class OcfVersionToOcfUnified(TestCase):
-                     automatic="1",
-                     on_target="0",
-                 ),
-+                ra.types.ResourceAgentActionOcf1_0(
-+                    name="action_4",
-+                    timeout=None,
-+                    interval=None,
-+                    role=None,
-+                    start_delay=None,
-+                    depth=None,
-+                    automatic="yes",
-+                    on_target="True",
-+                ),
-             ],
-         )
-         metadata_out = ra.ResourceAgentMetadata(
-@@ -289,6 +321,16 @@ class OcfVersionToOcfUnified(TestCase):
-                     automatic=True,
-                     on_target=False,
-                 ),
-+                ra.ResourceAgentAction(
-+                    name="action_4",
-+                    timeout=None,
-+                    interval=None,
-+                    role=None,
-+                    start_delay=None,
-+                    depth=None,
-+                    automatic=True,
-+                    on_target=True,
-+                ),
-             ],
-         )
-         self.assertEqual(
-diff --git a/pcs_test/tier0/lib/resource_agent/test_xml.py b/pcs_test/tier0/lib/resource_agent/test_xml.py
-index 26bbbb7d..ea055ee2 100644
---- a/pcs_test/tier0/lib/resource_agent/test_xml.py
-+++ b/pcs_test/tier0/lib/resource_agent/test_xml.py
-@@ -164,8 +164,13 @@ class MetadataXmlToDom(TestCase):
-             ra.xml._metadata_xml_to_dom("not an xml")
- 
-     def test_no_version_not_valid(self):
--        with self.assertRaises(etree.DocumentInvalid):
--            ra.xml._metadata_xml_to_dom("<resource-agent/>")
-+        # pylint: disable=no-self-use
-+        metadata = """
-+            <resource-agent/>
-+        """
-+        assert_xml_equal(
-+            metadata, etree_to_str(ra.xml._metadata_xml_to_dom(metadata))
-+        )
- 
-     def test_no_version_valid(self):
-         # pylint: disable=no-self-use
-@@ -178,14 +183,15 @@ class MetadataXmlToDom(TestCase):
-         )
- 
-     def test_ocf_1_0_not_valid(self):
--        with self.assertRaises(etree.DocumentInvalid):
--            ra.xml._metadata_xml_to_dom(
--                """
--                    <resource-agent>
--                        <version>1.0</version>
--                    </resource-agent>
--                """
--            )
-+        # pylint: disable=no-self-use
-+        metadata = """
-+            <resource-agent>
-+                <version>1.0</version>
-+            </resource-agent>
-+        """
-+        assert_xml_equal(
-+            metadata, etree_to_str(ra.xml._metadata_xml_to_dom(metadata))
-+        )
- 
-     def test_ocf_1_0_valid(self):
-         # pylint: disable=no-self-use
-@@ -273,19 +279,16 @@ class LoadMetadata(TestCase):
- 
-     def test_not_valid_xml(self):
-         agent_name = ra.ResourceAgentName("ocf", "pacemaker", "Dummy")
-+        metadata = "<resource-agent/>"
-         self.config.runner.pcmk.load_agent(
-             agent_name="ocf:pacemaker:Dummy",
--            stdout="<resource-agent/>",
-+            stdout=metadata,
-         )
- 
-         env = self.env_assist.get_env()
--        with self.assertRaises(ra.UnableToGetAgentMetadata) as cm:
--            ra.xml.load_metadata(env.cmd_runner(), agent_name)
--        self.assertEqual(cm.exception.agent_name, "ocf:pacemaker:Dummy")
--        self.assertTrue(
--            cm.exception.message.startswith(
--                "Element resource-agent failed to validate"
--            )
-+        assert_xml_equal(
-+            metadata,
-+            etree_to_str(ra.xml.load_metadata(env.cmd_runner(), agent_name)),
-         )
- 
- 
-@@ -335,16 +338,15 @@ class LoadFakeAgentMetadata(TestCase):
- 
-     def test_not_valid_xml(self):
-         agent_name = ra.const.PACEMAKER_FENCED
--        self.config.runner.pcmk.load_fenced_metadata(stdout="<resource-agent/>")
-+        metadata = "<resource-agent/>"
-+        self.config.runner.pcmk.load_fenced_metadata(stdout=metadata)
- 
-         env = self.env_assist.get_env()
--        with self.assertRaises(ra.UnableToGetAgentMetadata) as cm:
--            ra.xml.load_fake_agent_metadata(env.cmd_runner(), agent_name)
--        self.assertEqual(cm.exception.agent_name, "pacemaker-fenced")
--        self.assertTrue(
--            cm.exception.message.startswith(
--                "Element resource-agent failed to validate"
--            )
-+        assert_xml_equal(
-+            metadata,
-+            etree_to_str(
-+                ra.xml.load_fake_agent_metadata(env.cmd_runner(), agent_name)
-+            ),
-         )
- 
- 
-@@ -549,19 +551,37 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
-         )
- 
-     def test_parameters_empty_parameter(self):
--        # parameters must have at least 'name' attribute
--        with self.assertRaises(ra.UnableToGetAgentMetadata):
--            self.parse(
--                self.xml(
--                    """
--                        <resource-agent>
--                            <parameters>
--                                <parameter/>
--                            </parameters>
--                        </resource-agent>
--                    """
--                )
--            )
-+        self.assert_parse_result(
-+            self.xml(
-+                """
-+                    <resource-agent>
-+                        <parameters>
-+                            <parameter/>
-+                        </parameters>
-+                    </resource-agent>
-+                """
-+            ),
-+            ResourceAgentMetadataOcf1_0(
-+                self.agent_name,
-+                shortdesc=None,
-+                longdesc=None,
-+                parameters=[
-+                    ResourceAgentParameterOcf1_0(
-+                        name="",
-+                        shortdesc=None,
-+                        longdesc=None,
-+                        type="string",
-+                        default=None,
-+                        enum_values=None,
-+                        required=None,
-+                        deprecated=None,
-+                        obsoletes=None,
-+                        unique=None,
-+                    )
-+                ],
-+                actions=[],
-+            ),
-+        )
- 
-     def test_parameters_minimal(self):
-         self.assert_parse_result(
-@@ -708,19 +728,35 @@ class ParseOcf10BaseMixin(ParseOcfToolsMixin):
-         )
- 
-     def test_actions_empty_action(self):
--        # actions must have at least 'name' attribute
--        with self.assertRaises(ra.UnableToGetAgentMetadata):
--            self.parse(
--                self.xml(
--                    """
--                        <resource-agent>
--                            <actions>
--                                <action/>
--                            </actions>
--                        </resource-agent>
--                    """
--                )
--            )
-+        self.assert_parse_result(
-+            self.xml(
-+                """
-+                    <resource-agent>
-+                        <actions>
-+                            <action/>
-+                        </actions>
-+                    </resource-agent>
-+                """
-+            ),
-+            ResourceAgentMetadataOcf1_0(
-+                self.agent_name,
-+                shortdesc=None,
-+                longdesc=None,
-+                parameters=[],
-+                actions=[
-+                    ResourceAgentActionOcf1_0(
-+                        name="",
-+                        timeout=None,
-+                        interval=None,
-+                        role=None,
-+                        start_delay=None,
-+                        depth=None,
-+                        automatic=None,
-+                        on_target=None,
-+                    ),
-+                ],
-+            ),
-+        )
- 
-     def test_actions_multiple(self):
-         self.assert_parse_result(
-@@ -787,21 +823,6 @@ class ParseOcf10NoVersion(ParseOcf10BaseMixin, TestCase):
- class ParseOcf10UnsupportedVersion(ParseOcf10BaseMixin, TestCase):
-     ocf_version = "0.1.2"
- 
--    # These tests test that pcs raises an error if an agent doesn't conform to
--    # OCF schema. There is, however, no validation against OCF schema for
--    # agents with unsupported OCF version. That means no error message, pcs
--    # tries to process the agent and crashes. However bad that sounds, it's
--    # indended as that's how pcs behaved before OCF 1.1 was implemented.
--    # There's therefore no point in running these tests.
--
--    def test_parameters_empty_parameter(self):
--        # parameters must have at least 'name' attribute
--        pass
--
--    def test_actions_empty_action(self):
--        # actions must have at least 'name' attribute
--        pass
--
- 
- class ParseOcf10ExplicitVersion(ParseOcf10BaseMixin, TestCase):
-     ocf_version = "1.0"
--- 
-2.34.1
-
diff --git a/SOURCES/bz2115326-01-fix-pcs-quorum-device-remove.patch b/SOURCES/bz2115326-01-fix-pcs-quorum-device-remove.patch
new file mode 100644
index 0000000..73b32de
--- /dev/null
+++ b/SOURCES/bz2115326-01-fix-pcs-quorum-device-remove.patch
@@ -0,0 +1,25 @@
+From c00b5851a01361bd809a803ab88ff90d584d60e7 Mon Sep 17 00:00:00 2001
+From: Tomas Jelinek <tojeline@redhat.com>
+Date: Thu, 4 Aug 2022 15:05:41 +0200
+Subject: [PATCH 1/2] fix pcs quorum device remove
+
+---
+ pcsd/remote.rb | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/pcsd/remote.rb b/pcsd/remote.rb
+index b7bee7e6..74151190 100644
+--- a/pcsd/remote.rb
++++ b/pcsd/remote.rb
+@@ -2519,7 +2519,7 @@ def qdevice_net_client_destroy(param, request, auth_user)
+   end
+   stdout, stderr, retval = run_cmd(
+     auth_user,
+-    PCS, '--' 'qdevice', 'net-client', 'destroy'
++    PCS, '--', 'qdevice', 'net-client', 'destroy'
+   )
+   if retval != 0
+     return [400, stderr.join('')]
+-- 
+2.37.1
+
diff --git a/SOURCES/bz2117650-01-fix-enable-sbd-from-webui.patch b/SOURCES/bz2117650-01-fix-enable-sbd-from-webui.patch
new file mode 100644
index 0000000..2154b28
--- /dev/null
+++ b/SOURCES/bz2117650-01-fix-enable-sbd-from-webui.patch
@@ -0,0 +1,25 @@
+From ef08dbdc4f1fbf86cee3842eec5de32da5468609 Mon Sep 17 00:00:00 2001
+From: Ivan Devat <idevat@redhat.com>
+Date: Thu, 11 Aug 2022 16:06:29 +0200
+Subject: [PATCH 1/2] fix enable sbd from webui
+
+---
+ pcsd/remote.rb | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/pcsd/remote.rb b/pcsd/remote.rb
+index 74151190..a8aff853 100644
+--- a/pcsd/remote.rb
++++ b/pcsd/remote.rb
+@@ -2412,7 +2412,7 @@ def remote_enable_sbd(params, request, auth_user)
+   end
+ 
+   _, stderr, retcode = run_cmd(
+-    auth_user, PCS, *flags, '--' 'stonith', 'sbd', 'enable', *arg_list
++    auth_user, PCS, *flags, '--', 'stonith', 'sbd', 'enable', *arg_list
+   )
+ 
+   if retcode != 0
+-- 
+2.37.1
+
diff --git a/SOURCES/do-not-support-cluster-setup-with-udp-u-transport.patch b/SOURCES/do-not-support-cluster-setup-with-udp-u-transport.patch
index a25b509..98f21f0 100644
--- a/SOURCES/do-not-support-cluster-setup-with-udp-u-transport.patch
+++ b/SOURCES/do-not-support-cluster-setup-with-udp-u-transport.patch
@@ -1,7 +1,7 @@
-From f7230b92c946add84ed6072c20a4df5d97c77de2 Mon Sep 17 00:00:00 2001
+From d332704bb952b2b990688e469a4b209bf051be46 Mon Sep 17 00:00:00 2001
 From: Ivan Devat <idevat@redhat.com>
 Date: Tue, 20 Nov 2018 15:03:56 +0100
-Subject: [PATCH 3/3] do not support cluster setup with udp(u) transport
+Subject: [PATCH 2/2] do not support cluster setup with udp(u) transport
 
 ---
  pcs/pcs.8.in              | 2 ++
@@ -10,7 +10,7 @@ Subject: [PATCH 3/3] do not support cluster setup with udp(u) transport
  3 files changed, 6 insertions(+)
 
 diff --git a/pcs/pcs.8.in b/pcs/pcs.8.in
-index 05ee66db..101d66f7 100644
+index 6a00db89..97394e96 100644
 --- a/pcs/pcs.8.in
 +++ b/pcs/pcs.8.in
 @@ -436,6 +436,8 @@ By default, encryption is enabled with cipher=aes256 and hash=sha256. To disable
@@ -23,10 +23,10 @@ index 05ee66db..101d66f7 100644
  .br
  Transport options are: ip_version, netmtu
 diff --git a/pcs/usage.py b/pcs/usage.py
-index 78bb5ee7..b6f3dd10 100644
+index e76bf8e5..2e721dbd 100644
 --- a/pcs/usage.py
 +++ b/pcs/usage.py
-@@ -890,6 +890,7 @@ Commands:
+@@ -1002,6 +1002,7 @@ Commands:
              hash=sha256. To disable encryption, set cipher=none and hash=none.
  
          Transports udp and udpu:
@@ -49,5 +49,5 @@ index 2f26e831..a7702ac4 100644
  #csetup-transport-options.knet .without-knet
  {
 -- 
-2.34.1
+2.37.1
 
diff --git a/SOURCES/simplify-ternar-expression.patch b/SOURCES/simplify-ternar-expression.patch
deleted file mode 100644
index 0835fbd..0000000
--- a/SOURCES/simplify-ternar-expression.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From f44cdc871a39da3960bd04565b4d1d5ffa19bd23 Mon Sep 17 00:00:00 2001
-From: Ivan Devat <idevat@redhat.com>
-Date: Thu, 20 Jan 2022 13:32:49 +0100
-Subject: [PATCH 1/2] simplify ternar expression
-
-The motivation for this is that covscan complains about it.
----
- src/app/view/share/useUrlTabs.ts | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/app/view/share/useUrlTabs.ts b/src/app/view/share/useUrlTabs.ts
-index 7278dad8..a1136bf3 100644
---- a/src/app/view/share/useUrlTabs.ts
-+++ b/src/app/view/share/useUrlTabs.ts
-@@ -13,7 +13,7 @@ export const useUrlTabs = <TABS extends ReadonlyArray<string>>(
- 
-   return {
-     currentTab,
--    matchedContext: tab !== null ? tab.matched : `/${defaultTab}`,
-+    matchedContext: tab?.matched ?? `/${defaultTab}`,
-     tabList,
-   };
- };
--- 
-2.31.1
-
diff --git a/SPECS/pcs.spec b/SPECS/pcs.spec
index e5441aa..1a3f15d 100644
--- a/SPECS/pcs.spec
+++ b/SPECS/pcs.spec
@@ -1,6 +1,6 @@
 Name: pcs
-Version: 0.10.12
-Release: 6%{?dist}
+Version: 0.10.14
+Release: 4%{?dist}
 # https://docs.fedoraproject.org/en-US/packaging-guidelines/LicensingGuidelines/
 # https://fedoraproject.org/wiki/Licensing:Main?rd=Licensing#Good_Licenses
 # GPLv2: pcs
@@ -20,13 +20,13 @@ Summary: Pacemaker Configuration System
 ExclusiveArch: i686 x86_64 s390x ppc64le aarch64
 
 %global version_or_commit %{version}
-# %%global version_or_commit %%{version}.22-9d83b
+# %%global version_or_commit %%{version}.48-15d27
 
 %global pcs_source_name %{name}-%{version_or_commit}
 
 # ui_commit can be determined by hash, tag or branch
-%global ui_commit 0.1.12
-%global ui_modules_version 0.1.12
+%global ui_commit 0.1.13
+%global ui_modules_version 0.1.13
 %global ui_src_name pcs-web-ui-%{ui_commit}
 
 %global pcs_snmp_pkg_name  pcs-snmp
@@ -36,21 +36,21 @@ ExclusiveArch: i686 x86_64 s390x ppc64le aarch64
 %global dataclasses_version 0.8
 %global dacite_version  1.6.0
 %global dateutil_version  2.8.1
-%global version_rubygem_backports  3.17.2
-%global version_rubygem_daemons  1.3.1
-%global version_rubygem_ethon  0.12.0
+%global version_rubygem_backports  3.23.0
+%global version_rubygem_daemons  1.4.1
+%global version_rubygem_ethon  0.15.0
 %global version_rubygem_eventmachine  1.2.7
-%global version_rubygem_ffi  1.13.1
-%global version_rubygem_json  2.3.0
+%global version_rubygem_ffi  1.15.5
+%global version_rubygem_json  2.6.2
 %global version_rubygem_mustermann  1.1.1
 %global version_rubygem_open4  1.3.4
-%global version_rubygem_rack  2.2.3
-%global version_rubygem_rack_protection  2.0.8.1
+%global version_rubygem_rack  2.2.3.1
+%global version_rubygem_rack_protection  2.2.0
 %global version_rubygem_rack_test  1.1.0
 %global version_rubygem_rexml  3.2.5
-%global version_rubygem_ruby2_keywords  0.0.2
-%global version_rubygem_sinatra  2.0.8.1
-%global version_rubygem_thin  1.7.2
+%global version_rubygem_ruby2_keywords  0.0.5
+%global version_rubygem_sinatra  2.2.0
+%global version_rubygem_thin  1.8.1
 %global version_rubygem_tilt  2.0.10
 
 # javascript bundled libraries for old web-ui
@@ -118,23 +118,17 @@ Source101: https://github.com/ClusterLabs/pcs-web-ui/releases/download/%{ui_modu
 
 # pcs patches: <= 200
 # Patch1: bzNUMBER-01-name.patch
-Patch1: bz2028902-01-fix-enabling-corosync-qdevice.patch
-Patch2: bz1384485-01-fix-rsc-update-cmd-when-unable-to-get-agent-metadata.patch
-Patch3: bz2032997-01-skip-checking-of-scsi-devices-to-be-removed.patch
-Patch4: bz2036633-01-Make-ocf-linbit-drbd-agent-pass-OCF-validation.patch
-Patch5: bz1990784-01-Multiple-fixes-of-pcs-resource-move-autodelete-comma.patch
-Patch6: bz2022463-01-fix-creating-empty-cib.patch
-Patch7: bz2047983-01-Fix-snmp-client.patch
-Patch8: bz2050274-01-process-invalid-OCF-agents-the-same-way-as-before.patch
-Patch9: bz2050274-02-relax-OCF-1.0-parser.patch
+Patch1: bz1786964-01-code-formatting.patch
+Patch2: bz1786964-02-make-booth-ticket-mode-value-case-insensitive.patch
+Patch3: bz1791670-01-booth-sync-check-whether-etc-booth-exists.patch
+Patch4: bz2115326-01-fix-pcs-quorum-device-remove.patch
+Patch5: bz2117650-01-fix-enable-sbd-from-webui.patch
 
 # Downstream patches do not come from upstream. They adapt pcs for specific
 # RHEL needs.
 Patch101: do-not-support-cluster-setup-with-udp-u-transport.patch
 
 # ui patches: >200
-Patch201: simplify-ternar-expression.patch
-Patch202: bz2044409-01-fix-backend-parameter-all-in-cluster-destroy.patch
 
 # git for patches
 BuildRequires: git-core
@@ -322,8 +316,6 @@ 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 %{PATCH201}
-update_times_patch %{PATCH202}
 
 # patch pcs sources
 %autosetup -S git -n %{pcs_source_name} -N
@@ -334,10 +326,6 @@ update_times_patch %{PATCH2}
 update_times_patch %{PATCH3}
 update_times_patch %{PATCH4}
 update_times_patch %{PATCH5}
-update_times_patch %{PATCH6}
-update_times_patch %{PATCH7}
-update_times_patch %{PATCH8}
-update_times_patch %{PATCH9}
 update_times_patch %{PATCH101}
 
 cp -f %SOURCE1 %{pcsd_public_dir}/images
@@ -587,6 +575,34 @@ remove_all_tests
 %license pyagentx_LICENSE.txt
 
 %changelog
+* Wed Aug 17 2022 Miroslav Lisik <mlisik@redhat.com> - 0.10.14-4
+- Fixed enable sbd from webui
+- Resolves: rhbz#2117650
+
+* Mon Aug 08 2022 Miroslav Lisik <mlisik@redhat.com> - 0.10.14-3
+- Fixed `pcs quorum device remove`
+- Resolves: rhbz#2115326
+
+* Thu Jul 28 2022 Miroslav Lisik <mlisik@redhat.com> - 0.10.14-2
+- Fixed booth ticket mode value case insensitive
+- Fixed booth sync check whether /etc/booth exists
+- Resolves: rhbz#1786964 rhbz#1791670
+
+* Fri Jun 24 2022 Miroslav Lisik <mlisik@redhat.com> - 0.10.14-1
+- Rebased to latest upstream sources (see CHANGELOG.md)
+- Updated bundled rubygems: rack
+- Resolves: rhbz#2059500 rhbz#2096787 rhbz#2097383 rhbz#2097391 rhbz#2097392 rhbz#2097393
+
+* Tue May 24 2022 Miroslav Lisik <mlisik@redhat.com> - 0.10.13-1
+- Rebased to latest upstream sources (see CHANGELOG.md)
+- Updated pcs-web-ui
+- Updated bundled rubygems: backports, daemons, ethon ffi, json, ruby2_keywords, thin
+- Resolves: rhbz#1730232 rhbz#1786964 rhbz#1791661 rhbz#1791670 rhbz#1874624 rhbz#1909904 rhbz#1950551 rhbz#1954099 rhbz#2019894 rhbz#2023845 rhbz#2059500 rhbz#2064805 rhbz#2068456
+
+* Thu May 05 2022 Miroslav Lisik <mlisik@redhat.com> - 0.10.12-7
+- Updated bundled rubygems: sinatra, rack-protection
+- Resolves: rhbz#2081332
+
 * Fri Feb 11 2022 Miroslav Lisik <mlisik@redhat.com> - 0.10.12-6
 - Fixed processing agents not conforming to OCF schema
 - Resolves: rhbz#2050274