diff --git a/.gitignore b/.gitignore index 9b0615b..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-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/ruby2_keywords-0.0.5.gem SOURCES/sinatra-2.2.0.gem -SOURCES/thin-1.7.2.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 55cd790..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 +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 +d017b9e4d1978e0b3ccc3e2a31493809e4693cd3 SOURCES/ruby2_keywords-0.0.5.gem 5f0d7e63f9d8683f39ad23afe7e00b99602b87cc SOURCES/sinatra-2.2.0.gem -41395e86322ffd31f3a7aef1f697bda3e1e2d6b9 SOURCES/thin-1.7.2.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 -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 = """ - --- -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 +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("") + 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("") + 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("") + 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("") + 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 +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 +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 -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'' -+ -+ -+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""" -+ -+ {content} -+ -+ """ -+ -+ - def _rsc_primitive_fixture(res_id): - return f'' - -@@ -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 = '' -+ self.cib_without_constraint = ( -+ '' -+ ) - self.cib_simulate_constraint = ( - '' - ) -@@ -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_without_constraint = '' - 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): - - - """, -+ 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 = '' -+ self.cib_without_constraint = ( -+ '' -+ ) - 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'' -+ -+ -+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""" -+ -+ {content} -+ -+ """ -+ -+ -+nodes_section = _nodes_section_fixture( -+ _node_list_fixture(["node", "node1", "node2"]) -+) - resources_primitive = """ - - -@@ -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 -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 = """ -- -- -- -- -- -- -- -- --""" -+ empty_xml = """ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ """ - 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 -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 -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 -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. - - - -- -- -- boolean -- string -- integer -- second -- int -- time -- -- -+ -+ - -