From 92abc524faac8f8124789cb8a32d8374ea894429 Mon Sep 17 00:00:00 2001 From: lixin Date: Tue, 24 May 2022 17:25:32 +0800 Subject: [PATCH 1/5] Disallow setting order constraints on resources within same group --- pcs/common/reports/codes.py | 1 + pcs/common/reports/messages.py | 14 +++++++ pcs/constraint.py | 10 +++++ pcs/lib/cib/constraint/constraint.py | 2 + pcs/lib/cib/constraint/resource_set.py | 38 ++++++++++++++++++- pcs/utils.py | 13 +++++++ .../tier0/common/reports/test_messages.py | 8 ++++ 7 files changed, 85 insertions(+), 1 deletion(-) diff --git a/pcs/common/reports/codes.py b/pcs/common/reports/codes.py index fc0f0b73d..7bf0a9337 100644 --- a/pcs/common/reports/codes.py +++ b/pcs/common/reports/codes.py @@ -397,6 +397,7 @@ "RESOURCE_FOR_CONSTRAINT_IS_MULTIINSTANCE" ) RESOURCE_IN_BUNDLE_NOT_ACCESSIBLE = M("RESOURCE_IN_BUNDLE_NOT_ACCESSIBLE") +RESOURCE_IN_GROUP_CANNOT_SET_ORDER_CONSTRAINTS = M("RESOURCE_IN_GROUP_CANNOT_SET_ORDER_CONSTRAINTS") RESOURCE_INSTANCE_ATTR_VALUE_NOT_UNIQUE = M( "RESOURCE_INSTANCE_ATTR_VALUE_NOT_UNIQUE" ) diff --git a/pcs/common/reports/messages.py b/pcs/common/reports/messages.py index f147b846d..504770c88 100644 --- a/pcs/common/reports/messages.py +++ b/pcs/common/reports/messages.py @@ -347,6 +347,20 @@ def message(self) -> str: return "Resource set list is empty" +@dataclass(frozen=True) +class ResourceInGroupCannotSetOrderConstraints(ReportItemMessage): + """ + Can't set order constraint for resource in a group because the + start sequence is determined by it's location in the group + """ + + _code = codes.RESOURCE_IN_GROUP_CANNOT_SET_ORDER_CONSTRAINTS + + @property + def message(self) -> str: + return "Can't set order constraint for resource in a group" + + @dataclass(frozen=True) class RequiredOptionsAreMissing(ReportItemMessage): """ diff --git a/pcs/constraint.py b/pcs/constraint.py index 3e6e67fac..4a518e100 100644 --- a/pcs/constraint.py +++ b/pcs/constraint.py @@ -188,6 +188,14 @@ def _validate_constraint_resource(cib_dom, resource_id): utils.err(resource_error) +def _validate_resource_in_same_group(cib_dom, resource1, resource2): + test = utils.validate_resource_in_same_group(cib_dom, resource1, resource2) + if not test: + utils.err( + "unable to create constraint for resource in a same group" + ) + + # Syntax: colocation add [role] with [role] [score] [options] # possible commands: # with [score] [options] @@ -478,6 +486,8 @@ def _order_add(resource1, resource2, options_list, modifiers): _validate_constraint_resource(cib_dom, resource1) _validate_constraint_resource(cib_dom, resource2) + _validate_resource_in_same_group(cib_dom, resource1, resource2) + order_options = [] id_specified = False sym = None diff --git a/pcs/lib/cib/constraint/constraint.py b/pcs/lib/cib/constraint/constraint.py index 0695089ae..d8c83927c 100644 --- a/pcs/lib/cib/constraint/constraint.py +++ b/pcs/lib/cib/constraint/constraint.py @@ -186,5 +186,7 @@ def create_with_set(constraint_section, tag_name, options, resource_set_list): element = SubElement(constraint_section, tag_name) element.attrib.update(options) for resource_set_item in resource_set_list: + if tag_name == "rsc_order": + resource_set.is_resource_in_same_group(resource_set_item['ids']) resource_set.create(element, resource_set_item) return element diff --git a/pcs/lib/cib/constraint/resource_set.py b/pcs/lib/cib/constraint/resource_set.py index ee7e87c50..1e688e6ff 100644 --- a/pcs/lib/cib/constraint/resource_set.py +++ b/pcs/lib/cib/constraint/resource_set.py @@ -1,17 +1,23 @@ from lxml import etree +from pcs import utils from pcs.common import ( const, pacemaker, reports, ) +from pcs.common.reports.item import ReportItem from pcs.lib import validate from pcs.lib.cib.tools import ( are_new_role_names_supported, find_unique_id, + get_resources, + get_elements_by_ids, ) +from pcs.lib.cib.resource import group +from pcs.lib.cib.resource.common import get_parent_resource from pcs.lib.errors import LibraryError -from pcs.lib.xml_tools import export_attributes +from pcs.lib.xml_tools import export_attributes, etree_to_str, get_root _BOOLEAN_VALUES = ("true", "false") @@ -87,3 +93,33 @@ def export(element): "ids": get_resource_id_set_list(element), "options": export_attributes(element), } + + +def is_resource_in_same_group(resource_id_list): + env = utils.get_lib_env() + cib = env.get_cib() + resource_section = get_resources(cib) + + ( + resource_element_list, + id_not_found_list, + ) = get_elements_by_ids(get_root(resource_section), resource_id_list) + for resource_id in id_not_found_list: + raise LibraryError( + ReportItem.error(reports.messages.IdNotFound(resource_id, [])) + ) + + parent_list = [] + for resource_element in resource_element_list: + parent = get_parent_resource(resource_element) + if parent is not None and group.is_group(parent): + parent_list.append(parent) + + set_parent_list = set(parent_list) + if len(set_parent_list) == len(parent_list): + return + else: + raise LibraryError( + ReportItem.error(reports.messages.ResourceInGroupCannotSetOrderConstraints()) + ) + diff --git a/pcs/utils.py b/pcs/utils.py index 9915663ef..24d97300f 100644 --- a/pcs/utils.py +++ b/pcs/utils.py @@ -1424,6 +1424,19 @@ def validate_constraint_resource(dom, resource_id): return True, "", resource_id +def validate_resource_in_same_group(dom, resource1, resource2): + resource_el1 = dom_get_resource(dom, resource1) + resource_el2 = dom_get_resource(dom, resource2) + group1 = dom_get_parent_by_tag_names(resource_el1, ["group"]) + group2 = dom_get_parent_by_tag_names(resource_el2, ["group"]) + if not group1 or not group2: + return True + elif group1 == group2: + return False + else: + return True + + def dom_get_resource_remote_node_name(dom_resource): """ Commandline options: no options diff --git a/pcs_test/tier0/common/reports/test_messages.py b/pcs_test/tier0/common/reports/test_messages.py index a26e28aba..896979a89 100644 --- a/pcs_test/tier0/common/reports/test_messages.py +++ b/pcs_test/tier0/common/reports/test_messages.py @@ -118,6 +118,14 @@ def test_success(self): ) +class ResourceInGroupCannotSetOrderConstraints(NameBuildTest): + def test_success(self): + self.assert_message_from_report( + "Can't set order constraint for resource in a group", + reports.ResourceInGroupCannotSetOrderConstraints(), + ) + + class RequiredOptionsAreMissing(NameBuildTest): def test_build_message_with_type(self): self.assert_message_from_report( From c8089ab554584f0eb83a9caf85d6d3c85f37d98d Mon Sep 17 00:00:00 2001 From: lixin Date: Wed, 25 May 2022 17:59:25 +0800 Subject: [PATCH 2/5] fix circular import --- pcs/lib/cib/constraint/resource_set.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pcs/lib/cib/constraint/resource_set.py b/pcs/lib/cib/constraint/resource_set.py index 1e688e6ff..80e491587 100644 --- a/pcs/lib/cib/constraint/resource_set.py +++ b/pcs/lib/cib/constraint/resource_set.py @@ -1,6 +1,5 @@ from lxml import etree -from pcs import utils from pcs.common import ( const, pacemaker, @@ -96,6 +95,7 @@ def export(element): def is_resource_in_same_group(resource_id_list): + from pcs import utils env = utils.get_lib_env() cib = env.get_cib() resource_section = get_resources(cib) From d25ec8c18db383ddecbd3f2b89c8446d06c170a2 Mon Sep 17 00:00:00 2001 From: Tomas Jelinek Date: Tue, 24 May 2022 14:37:20 +0200 Subject: [PATCH 3/5] fix linter issues --- pcs/common/reports/codes.py | 4 +++- pcs/constraint.py | 4 +--- pcs/lib/cib/constraint/constraint.py | 2 +- pcs/lib/cib/constraint/resource_set.py | 20 +++++++++++--------- pcs/utils.py | 5 +---- 5 files changed, 17 insertions(+), 18 deletions(-) diff --git a/pcs/common/reports/codes.py b/pcs/common/reports/codes.py index 7bf0a9337..820019bc7 100644 --- a/pcs/common/reports/codes.py +++ b/pcs/common/reports/codes.py @@ -397,7 +397,9 @@ "RESOURCE_FOR_CONSTRAINT_IS_MULTIINSTANCE" ) RESOURCE_IN_BUNDLE_NOT_ACCESSIBLE = M("RESOURCE_IN_BUNDLE_NOT_ACCESSIBLE") -RESOURCE_IN_GROUP_CANNOT_SET_ORDER_CONSTRAINTS = M("RESOURCE_IN_GROUP_CANNOT_SET_ORDER_CONSTRAINTS") +RESOURCE_IN_GROUP_CANNOT_SET_ORDER_CONSTRAINTS = M( + "RESOURCE_IN_GROUP_CANNOT_SET_ORDER_CONSTRAINTS" +) RESOURCE_INSTANCE_ATTR_VALUE_NOT_UNIQUE = M( "RESOURCE_INSTANCE_ATTR_VALUE_NOT_UNIQUE" ) diff --git a/pcs/constraint.py b/pcs/constraint.py index 4a518e100..876642419 100644 --- a/pcs/constraint.py +++ b/pcs/constraint.py @@ -191,9 +191,7 @@ def _validate_constraint_resource(cib_dom, resource_id): def _validate_resource_in_same_group(cib_dom, resource1, resource2): test = utils.validate_resource_in_same_group(cib_dom, resource1, resource2) if not test: - utils.err( - "unable to create constraint for resource in a same group" - ) + utils.err("unable to create constraint for resource in a same group") # Syntax: colocation add [role] with [role] [score] [options] diff --git a/pcs/lib/cib/constraint/constraint.py b/pcs/lib/cib/constraint/constraint.py index d8c83927c..2869a0185 100644 --- a/pcs/lib/cib/constraint/constraint.py +++ b/pcs/lib/cib/constraint/constraint.py @@ -187,6 +187,6 @@ def create_with_set(constraint_section, tag_name, options, resource_set_list): element.attrib.update(options) for resource_set_item in resource_set_list: if tag_name == "rsc_order": - resource_set.is_resource_in_same_group(resource_set_item['ids']) + resource_set.is_resource_in_same_group(resource_set_item["ids"]) resource_set.create(element, resource_set_item) return element diff --git a/pcs/lib/cib/constraint/resource_set.py b/pcs/lib/cib/constraint/resource_set.py index 80e491587..60f050d30 100644 --- a/pcs/lib/cib/constraint/resource_set.py +++ b/pcs/lib/cib/constraint/resource_set.py @@ -7,16 +7,19 @@ ) from pcs.common.reports.item import ReportItem from pcs.lib import validate +from pcs.lib.cib.resource import group +from pcs.lib.cib.resource.common import get_parent_resource from pcs.lib.cib.tools import ( are_new_role_names_supported, find_unique_id, - get_resources, get_elements_by_ids, + get_resources, ) -from pcs.lib.cib.resource import group -from pcs.lib.cib.resource.common import get_parent_resource from pcs.lib.errors import LibraryError -from pcs.lib.xml_tools import export_attributes, etree_to_str, get_root +from pcs.lib.xml_tools import ( + export_attributes, + get_root, +) _BOOLEAN_VALUES = ("true", "false") @@ -116,10 +119,9 @@ def is_resource_in_same_group(resource_id_list): parent_list.append(parent) set_parent_list = set(parent_list) - if len(set_parent_list) == len(parent_list): - return - else: + if len(set_parent_list) != len(parent_list): raise LibraryError( - ReportItem.error(reports.messages.ResourceInGroupCannotSetOrderConstraints()) + ReportItem.error( + reports.messages.ResourceInGroupCannotSetOrderConstraints() + ) ) - diff --git a/pcs/utils.py b/pcs/utils.py index 24d97300f..1d9f05b42 100644 --- a/pcs/utils.py +++ b/pcs/utils.py @@ -1431,10 +1431,7 @@ def validate_resource_in_same_group(dom, resource1, resource2): group2 = dom_get_parent_by_tag_names(resource_el2, ["group"]) if not group1 or not group2: return True - elif group1 == group2: - return False - else: - return True + return group1 != group2 def dom_get_resource_remote_node_name(dom_resource): From dd396c9d99bb9cc6171ab0ca450a6b037f077c2a Mon Sep 17 00:00:00 2001 From: Tomas Jelinek Date: Tue, 24 May 2022 16:55:00 +0200 Subject: [PATCH 4/5] code review fixes Fix several crashes and bugs, clean up code --- CHANGELOG.md | 3 ++ pcs/common/reports/codes.py | 6 ++-- pcs/common/reports/messages.py | 12 ++++--- pcs/constraint.py | 13 ++++--- pcs/lib/cib/constraint/constraint.py | 10 ++++-- pcs/lib/cib/constraint/resource_set.py | 34 ++++++------------- pcs/utils.py | 11 ++++-- .../tier0/common/reports/test_messages.py | 6 ++-- 8 files changed, 50 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc0c860da..7b588a917 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,7 +23,10 @@ ([rhbz#2058243]) - Preventing fence-loop caused when stonith-watchdog-timeout is set with wrong value ([rhbz#2058246]) +- Do not allow to create an order constraint for resources in one group as that + may block Pacemaker ([ghpull#509]) +[ghpull#509]: https://github.com/ClusterLabs/pcs/pull/509 [rhbz#2024522]: https://bugzilla.redhat.com/show_bug.cgi?id=2024522 [rhbz#2053177]: https://bugzilla.redhat.com/show_bug.cgi?id=2053177 [rhbz#2054671]: https://bugzilla.redhat.com/show_bug.cgi?id=2054671 diff --git a/pcs/common/reports/codes.py b/pcs/common/reports/codes.py index 820019bc7..3b52b9d98 100644 --- a/pcs/common/reports/codes.py +++ b/pcs/common/reports/codes.py @@ -102,6 +102,9 @@ "CANNOT_MOVE_RESOURCE_STOPPED_NO_NODE_SPECIFIED" ) CANNOT_REMOVE_ALL_CLUSTER_NODES = M("CANNOT_REMOVE_ALL_CLUSTER_NODES") +CANNOT_SET_ORDER_CONSTRAINTS_FOR_RESOURCES_IN_THE_SAME_GROUP = M( + "CANNOT_SET_ORDER_CONSTRAINTS_FOR_RESOURCES_IN_THE_SAME_GROUP" +) CANNOT_UNMOVE_UNBAN_RESOURCE_MASTER_RESOURCE_NOT_PROMOTABLE = M( "CANNOT_UNMOVE_UNBAN_RESOURCE_MASTER_RESOURCE_NOT_PROMOTABLE" ) @@ -397,9 +400,6 @@ "RESOURCE_FOR_CONSTRAINT_IS_MULTIINSTANCE" ) RESOURCE_IN_BUNDLE_NOT_ACCESSIBLE = M("RESOURCE_IN_BUNDLE_NOT_ACCESSIBLE") -RESOURCE_IN_GROUP_CANNOT_SET_ORDER_CONSTRAINTS = M( - "RESOURCE_IN_GROUP_CANNOT_SET_ORDER_CONSTRAINTS" -) RESOURCE_INSTANCE_ATTR_VALUE_NOT_UNIQUE = M( "RESOURCE_INSTANCE_ATTR_VALUE_NOT_UNIQUE" ) diff --git a/pcs/common/reports/messages.py b/pcs/common/reports/messages.py index 504770c88..06d9bf6a0 100644 --- a/pcs/common/reports/messages.py +++ b/pcs/common/reports/messages.py @@ -348,17 +348,19 @@ def message(self) -> str: @dataclass(frozen=True) -class ResourceInGroupCannotSetOrderConstraints(ReportItemMessage): +class CannotSetOrderConstraintsForResourcesInTheSameGroup(ReportItemMessage): """ - Can't set order constraint for resource in a group because the - start sequence is determined by it's location in the group + Can't set order constraint for resources in one group because the + start sequence of the resources is determined by their location in the group """ - _code = codes.RESOURCE_IN_GROUP_CANNOT_SET_ORDER_CONSTRAINTS + _code = codes.CANNOT_SET_ORDER_CONSTRAINTS_FOR_RESOURCES_IN_THE_SAME_GROUP @property def message(self) -> str: - return "Can't set order constraint for resource in a group" + return ( + "Cannot create an order constraint for resources in the same group" + ) @dataclass(frozen=True) diff --git a/pcs/constraint.py b/pcs/constraint.py index 876642419..bdf0001c5 100644 --- a/pcs/constraint.py +++ b/pcs/constraint.py @@ -188,10 +188,13 @@ def _validate_constraint_resource(cib_dom, resource_id): utils.err(resource_error) -def _validate_resource_in_same_group(cib_dom, resource1, resource2): - test = utils.validate_resource_in_same_group(cib_dom, resource1, resource2) - if not test: - utils.err("unable to create constraint for resource in a same group") +def _validate_resources_not_in_same_group(cib_dom, resource1, resource2): + if not utils.validate_resources_not_in_same_group( + cib_dom, resource1, resource2 + ): + utils.err( + "Cannot create an order constraint for resources in the same group" + ) # Syntax: colocation add [role] with [role] [score] [options] @@ -484,7 +487,7 @@ def _order_add(resource1, resource2, options_list, modifiers): _validate_constraint_resource(cib_dom, resource1) _validate_constraint_resource(cib_dom, resource2) - _validate_resource_in_same_group(cib_dom, resource1, resource2) + _validate_resources_not_in_same_group(cib_dom, resource1, resource2) order_options = [] id_specified = False diff --git a/pcs/lib/cib/constraint/constraint.py b/pcs/lib/cib/constraint/constraint.py index 2869a0185..276df75fc 100644 --- a/pcs/lib/cib/constraint/constraint.py +++ b/pcs/lib/cib/constraint/constraint.py @@ -24,6 +24,7 @@ from pcs.lib.xml_tools import ( export_attributes, find_parent, + get_root, ) @@ -185,8 +186,13 @@ def create_with_set(constraint_section, tag_name, options, resource_set_list): ) element = SubElement(constraint_section, tag_name) element.attrib.update(options) + if tag_name == "rsc_order": + all_resource_ids = [] + for resource_set_item in resource_set_list: + all_resource_ids.extend(resource_set_item["ids"]) + resource_set.is_resource_in_same_group( + get_root(constraint_section), all_resource_ids + ) for resource_set_item in resource_set_list: - if tag_name == "rsc_order": - resource_set.is_resource_in_same_group(resource_set_item["ids"]) resource_set.create(element, resource_set_item) return element diff --git a/pcs/lib/cib/constraint/resource_set.py b/pcs/lib/cib/constraint/resource_set.py index 60f050d30..db0070f85 100644 --- a/pcs/lib/cib/constraint/resource_set.py +++ b/pcs/lib/cib/constraint/resource_set.py @@ -13,13 +13,9 @@ are_new_role_names_supported, find_unique_id, get_elements_by_ids, - get_resources, ) from pcs.lib.errors import LibraryError -from pcs.lib.xml_tools import ( - export_attributes, - get_root, -) +from pcs.lib.xml_tools import export_attributes _BOOLEAN_VALUES = ("true", "false") @@ -97,31 +93,21 @@ def export(element): } -def is_resource_in_same_group(resource_id_list): - from pcs import utils - env = utils.get_lib_env() - cib = env.get_cib() - resource_section = get_resources(cib) - - ( - resource_element_list, - id_not_found_list, - ) = get_elements_by_ids(get_root(resource_section), resource_id_list) - for resource_id in id_not_found_list: - raise LibraryError( - ReportItem.error(reports.messages.IdNotFound(resource_id, [])) - ) +def is_resource_in_same_group(cib, resource_id_list): + # We don't care about not found elements here, that is a job of another + # validator. We do not care if the id doesn't belong to a resource either + # for the same reason. + element_list, _ = get_elements_by_ids(cib, resource_id_list) parent_list = [] - for resource_element in resource_element_list: - parent = get_parent_resource(resource_element) + for element in element_list: + parent = get_parent_resource(element) if parent is not None and group.is_group(parent): parent_list.append(parent) - set_parent_list = set(parent_list) - if len(set_parent_list) != len(parent_list): + if len(set(parent_list)) != len(parent_list): raise LibraryError( ReportItem.error( - reports.messages.ResourceInGroupCannotSetOrderConstraints() + reports.messages.CannotSetOrderConstraintsForResourcesInTheSameGroup() ) ) diff --git a/pcs/utils.py b/pcs/utils.py index 1d9f05b42..3d1119f47 100644 --- a/pcs/utils.py +++ b/pcs/utils.py @@ -1424,9 +1424,14 @@ def validate_constraint_resource(dom, resource_id): return True, "", resource_id -def validate_resource_in_same_group(dom, resource1, resource2): - resource_el1 = dom_get_resource(dom, resource1) - resource_el2 = dom_get_resource(dom, resource2) +def validate_resources_not_in_same_group(dom, resource_id1, resource_id2): + resource_el1 = dom_get_resource(dom, resource_id1) + resource_el2 = dom_get_resource(dom, resource_id2) + if not resource_el1 or not resource_el2: + # Only primitive resources can be in a group. If at least one of the + # resources is not a primitive (resource_el is None), then the + # resources are not in the same group. + return True group1 = dom_get_parent_by_tag_names(resource_el1, ["group"]) group2 = dom_get_parent_by_tag_names(resource_el2, ["group"]) if not group1 or not group2: diff --git a/pcs_test/tier0/common/reports/test_messages.py b/pcs_test/tier0/common/reports/test_messages.py index 896979a89..206cbfb41 100644 --- a/pcs_test/tier0/common/reports/test_messages.py +++ b/pcs_test/tier0/common/reports/test_messages.py @@ -118,11 +118,11 @@ def test_success(self): ) -class ResourceInGroupCannotSetOrderConstraints(NameBuildTest): +class CannotSetOrderConstraintsForResourcesInTheSameGroup(NameBuildTest): def test_success(self): self.assert_message_from_report( - "Can't set order constraint for resource in a group", - reports.ResourceInGroupCannotSetOrderConstraints(), + "Cannot create an order constraint for resources in the same group", + reports.CannotSetOrderConstraintsForResourcesInTheSameGroup(), ) From a00f4a0bb6464f84feeaf1fc2a8da455e0cf97ea Mon Sep 17 00:00:00 2001 From: Tomas Jelinek Date: Wed, 25 May 2022 17:02:54 +0200 Subject: [PATCH 5/5] add tests --- pcs/lib/cib/constraint/resource_set.py | 2 +- pcs_test/Makefile.am | 1 + .../lib/commands/test_constraint_order.py | 81 +++++++++++++++++++ pcs_test/tier1/legacy/test_constraints.py | 46 +++++++++++ 4 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 pcs_test/tier0/lib/commands/test_constraint_order.py diff --git a/pcs/lib/cib/constraint/resource_set.py b/pcs/lib/cib/constraint/resource_set.py index db0070f85..8862cb58d 100644 --- a/pcs/lib/cib/constraint/resource_set.py +++ b/pcs/lib/cib/constraint/resource_set.py @@ -97,7 +97,7 @@ def is_resource_in_same_group(cib, resource_id_list): # We don't care about not found elements here, that is a job of another # validator. We do not care if the id doesn't belong to a resource either # for the same reason. - element_list, _ = get_elements_by_ids(cib, resource_id_list) + element_list, _ = get_elements_by_ids(cib, set(resource_id_list)) parent_list = [] for element in element_list: diff --git a/pcs_test/Makefile.am b/pcs_test/Makefile.am index 4fbce43b1..da96612ac 100644 --- a/pcs_test/Makefile.am +++ b/pcs_test/Makefile.am @@ -230,6 +230,7 @@ EXTRA_DIST = \ tier0/lib/commands/test_booth.py \ tier0/lib/commands/test_cib_options.py \ tier0/lib/commands/test_constraint_common.py \ + tier0/lib/commands/test_constraint_order.py \ tier0/lib/commands/test_fencing_topology.py \ tier0/lib/commands/test_node.py \ tier0/lib/commands/test_pcsd.py \ diff --git a/pcs_test/tier0/lib/commands/test_constraint_order.py b/pcs_test/tier0/lib/commands/test_constraint_order.py new file mode 100644 index 000000000..c2d33a15f --- /dev/null +++ b/pcs_test/tier0/lib/commands/test_constraint_order.py @@ -0,0 +1,81 @@ +from unittest import TestCase + +from pcs.common import reports +from pcs.lib.commands import constraint + +from pcs_test.tools import fixture +from pcs_test.tools.command_env import get_env_tools + + +class SetCreate(TestCase): + def setUp(self): + resources_xml = """ + + + + + + + + + + + """ + self.env_assist, self.config = get_env_tools(self) + self.config.runner.cib.load(resources=resources_xml) + + def test_deny_resources_from_one_group_in_one_set(self): + self.env_assist.assert_raise_library_error( + lambda: constraint.order.create_with_set( + self.env_assist.get_env(), + [{"ids": ["A", "B"], "options": {}}], + {}, + ), + [ + fixture.error( + reports.codes.CANNOT_SET_ORDER_CONSTRAINTS_FOR_RESOURCES_IN_THE_SAME_GROUP, + ) + ], + expected_in_processor=False, + ) + + def test_deny_resources_from_one_group_in_different_sets(self): + self.env_assist.assert_raise_library_error( + lambda: constraint.order.create_with_set( + self.env_assist.get_env(), + [{"ids": ["A"], "options": {}}, {"ids": ["B"], "options": {}}], + {}, + ), + [ + fixture.error( + reports.codes.CANNOT_SET_ORDER_CONSTRAINTS_FOR_RESOURCES_IN_THE_SAME_GROUP, + ) + ], + expected_in_processor=False, + ) + + def test_allow_resources_from_different_groups(self): + constraints_xml = """ + + + + + + + + + + + + + """ + self.config.env.push_cib(constraints=constraints_xml) + + constraint.order.create_with_set( + self.env_assist.get_env(), + [ + {"ids": ["A", "C"], "options": {}}, + {"ids": ["A", "D"], "options": {}}, + ], + {}, + ) diff --git a/pcs_test/tier1/legacy/test_constraints.py b/pcs_test/tier1/legacy/test_constraints.py index 7755a78f9..ea5c880d0 100644 --- a/pcs_test/tier1/legacy/test_constraints.py +++ b/pcs_test/tier1/legacy/test_constraints.py @@ -5377,3 +5377,49 @@ def test_complex_primitive_all(self): """ ), ) + + +class OrderVsGroup(unittest.TestCase, AssertPcsMixin): + empty_cib = rc("cib-empty.xml") + + def setUp(self): + self.temp_cib = get_tmp_file("tier1_constraint_order_vs_group") + write_file_to_tmpfile(self.empty_cib, self.temp_cib) + self.pcs_runner = PcsRunner(self.temp_cib.name) + self.assert_pcs_success( + "resource create A ocf:heartbeat:Dummy --group grAB".split() + ) + self.assert_pcs_success( + "resource create B ocf:heartbeat:Dummy --group grAB".split() + ) + self.assert_pcs_success( + "resource create C ocf:heartbeat:Dummy --group grC".split() + ) + self.assert_pcs_success("resource create D ocf:heartbeat:Dummy".split()) + + def tearDown(self): + self.temp_cib.close() + + def test_deny_resources_in_one_group(self): + self.assert_pcs_fail( + "constraint order A then B".split(), + "Error: Cannot create an order constraint for resources in the same group\n", + ) + + def test_allow_resources_in_different_groups(self): + self.assert_pcs_success( + "constraint order A then C".split(), + "Adding A C (kind: Mandatory) (Options: first-action=start then-action=start)\n", + ) + + def test_allow_grouped_and_not_grouped_resource(self): + self.assert_pcs_success( + "constraint order A then D".split(), + "Adding A D (kind: Mandatory) (Options: first-action=start then-action=start)\n", + ) + + def test_allow_group_and_resource(self): + self.assert_pcs_success( + "constraint order grAB then C".split(), + "Adding grAB C (kind: Mandatory) (Options: first-action=start then-action=start)\n", + )