From 7c56001aa76c4a5f69f29b328061c419c7ce856b Mon Sep 17 00:00:00 2001 From: Peter Romancik Date: Thu, 1 Feb 2024 17:17:40 +0100 Subject: [PATCH 1/2] further fixes after review --- pcs/common/reports/codes.py | 2 +- pcs/common/reports/messages.py | 8 +- pcs/lib/pacemaker/status.py | 85 ++++++++++--------- .../tier0/common/reports/test_messages.py | 14 +-- pcs_test/tier0/lib/commands/test_status.py | 4 +- pcs_test/tier0/lib/pacemaker/test_status.py | 68 ++++++++------- 6 files changed, 97 insertions(+), 84 deletions(-) diff --git a/pcs/common/reports/codes.py b/pcs/common/reports/codes.py index f9614331..e967d0b1 100644 --- a/pcs/common/reports/codes.py +++ b/pcs/common/reports/codes.py @@ -50,7 +50,7 @@ AGENT_SELF_VALIDATION_SKIPPED_UPDATED_RESOURCE_MISCONFIGURED = M( ) AGENT_SELF_VALIDATION_RESULT = M("AGENT_SELF_VALIDATION_RESULT") BAD_CLUSTER_STATE_FORMAT = M("BAD_CLUSTER_STATE_FORMAT") -BAD_CLUSTER_STATE = M("BAD_CLUSTER_STATE") +BAD_CLUSTER_STATE_DATA = M("BAD_CLUSTER_STATE_DATA") BOOTH_ADDRESS_DUPLICATION = M("BOOTH_ADDRESS_DUPLICATION") BOOTH_ALREADY_IN_CIB = M("BOOTH_ALREADY_IN_CIB") BOOTH_AUTHFILE_NOT_USED = M("BOOTH_AUTHFILE_NOT_USED") diff --git a/pcs/common/reports/messages.py b/pcs/common/reports/messages.py index 8b9bc63e..53f15170 100644 --- a/pcs/common/reports/messages.py +++ b/pcs/common/reports/messages.py @@ -3277,7 +3277,7 @@ class BadClusterStateFormat(ReportItemMessage): @dataclass(frozen=True) -class BadClusterState(ReportItemMessage): +class BadClusterStateData(ReportItemMessage): """ crm_mon xml output is invalid despite conforming to the schema @@ -3285,13 +3285,13 @@ class BadClusterState(ReportItemMessage): """ reason: Optional[str] = None - _code = codes.BAD_CLUSTER_STATE + _code = codes.BAD_CLUSTER_STATE_DATA @property def message(self) -> str: return ( "Cannot load cluster status, xml does not describe valid cluster " - f"status{format_optional(self.reason, template=': {}')}." + f"status{format_optional(self.reason, template=': {}')}" ) @@ -3314,7 +3314,7 @@ class ClusterStatusBundleMemberIdAsImplicit(ReportItemMessage): return ( "Skipping bundle '{bundle_id}': {resource_word} " "{bad_ids} {has} the same id as some of the " - "implicit bundle resources." + "implicit bundle resources" ).format( bundle_id=self.bundle_id, resource_word=format_plural(self.bad_ids, "resource"), diff --git a/pcs/lib/pacemaker/status.py b/pcs/lib/pacemaker/status.py index a86ede55..deb8aa0d 100644 --- a/pcs/lib/pacemaker/status.py +++ b/pcs/lib/pacemaker/status.py @@ -2,7 +2,6 @@ from collections import Counter from typing import ( Optional, Sequence, - Union, cast, ) @@ -60,11 +59,13 @@ class UnexpectedMemberError(ClusterStatusParsingError): resource_id: str, resource_type: str, member_id: str, + member_type: str, expected_types: list[str], ): super().__init__(resource_id) self.resource_type = resource_type self.member_id = member_id + self.member_type = member_type self.expected_types = expected_types @@ -106,46 +107,44 @@ def cluster_status_parsing_error_to_report( ) -> reports.ReportItem: reason = "" if isinstance(e, EmptyResourceIdError): - reason = "Resource with empty id." + reason = "Resource with an empty id" elif isinstance(e, EmptyNodeNameError): reason = ( - f"Resource with id '{e.resource_id}' contains node with empty name." + f"Resource '{e.resource_id}' contains a node with an empty name" ) elif isinstance(e, UnknownPcmkRoleError): reason = ( - f"Resource with id '{e.resource_id}' contains unknown " - f"pcmk role '{e.role}'." + f"Resource '{e.resource_id}' contains an unknown " + f"role '{e.role}'" ) elif isinstance(e, UnexpectedMemberError): reason = ( - f"Unexpected resource '{e.member_id}' inside of resource " - f"'{e.resource_id}' of type '{e.resource_type}'. " - f"Only resources of type {format_list(e.expected_types, '|')} " - f"can be in {e.resource_type}." + f"Unexpected resource '{e.member_id}' of type '{e.member_type}' " + f"inside of resource '{e.resource_id}' of type '{e.resource_type}'." + f" Only resources of type {format_list(e.expected_types)} " + f"can be in a {e.resource_type}" ) elif isinstance(e, MixedMembersError): - reason = ( - f"Primitive and group members mixed in clone '{e.resource_id}'." - ) + reason = f"Primitive and group members mixed in clone '{e.resource_id}'" elif isinstance(e, DifferentMemberIdsError): - reason = f"Members with different ids in resource '{e.resource_id}'." + reason = f"Members with different ids in clone '{e.resource_id}'" elif isinstance(e, BundleReplicaMissingImplicitResourceError): reason = ( f"Replica '{e.replica_id}' of bundle '{e.resource_id}' " - f"is missing implicit {e.implicit_type} resource." + f"is missing implicit {e.implicit_type} resource" ) elif isinstance(e, BundleReplicaInvalidMemberCountError): reason = ( f"Replica '{e.replica_id}' of bundle '{e.resource_id}' has " - "invalid number of members." + "invalid number of members" ) elif isinstance(e, BundleDifferentReplicas): - reason = f"Replicas of bundle '{e.resource_id}' are not the same." + reason = f"Replicas of bundle '{e.resource_id}' are not the same" return reports.ReportItem( reports.ReportItemSeverity.error(), - reports.messages.BadClusterState(reason), + reports.messages.BadClusterStateData(reason), ) @@ -160,7 +159,7 @@ def _primitive_to_dto( target_role = _get_target_role(primitive_el) node_names = [ - str(node.get("name")) for node in primitive_el.iterfind("node") + str(node.attrib["name"]) for node in primitive_el.iterfind("node") ] if node_names and any(not name for name in node_names): @@ -168,7 +167,7 @@ def _primitive_to_dto( return PrimitiveStatusDto( resource_id, - str(primitive_el.get("resource_agent")), + str(primitive_el.attrib["resource_agent"]), role, target_role, is_true(primitive_el.get("active", "false")), @@ -179,7 +178,7 @@ def _primitive_to_dto( is_true(primitive_el.get("failed", "false")), is_true(primitive_el.get("managed", "false")), is_true(primitive_el.get("failure_ignored", "false")), - [str(node.get("name")) for node in primitive_el.iterfind("node")], + node_names, primitive_el.get("pending"), primitive_el.get("locked_to"), ) @@ -197,7 +196,11 @@ def _group_to_dto( member_list.append(_primitive_to_dto(member, remove_clone_suffix)) else: raise UnexpectedMemberError( - group_id, "group", str(member.get("id")), ["primitive"] + group_id, + "group", + str(member.attrib["id"]), + member.tag, + ["primitive"], ) return GroupStatusDto( @@ -228,29 +231,28 @@ def _clone_to_dto( group_list.append(_group_to_dto(member, is_unique)) else: raise UnexpectedMemberError( - clone_id, "clone", str(member.get("id")), ["primitive", "group"] + clone_id, + "clone", + str(member.attrib["id"]), + member.tag, + ["primitive", "group"], ) if primitive_list and group_list: raise MixedMembersError(clone_id) - instance_list: Union[list[PrimitiveStatusDto], list[GroupStatusDto]] if primitive_list: if len(set(res.resource_id for res in primitive_list)) > 1: raise DifferentMemberIdsError(clone_id) - instance_list = primitive_list - else: + if group_list: group_ids = set(group.resource_id for group in group_list) children_ids = set( tuple(child.resource_id for child in group.members) for group in group_list ) - if len(group_ids) > 1 or len(children_ids) > 1: raise DifferentMemberIdsError(clone_id) - instance_list = group_list - return CloneStatusDto( clone_id, is_true(clone_el.get("multi_state", "false")), @@ -262,7 +264,7 @@ def _clone_to_dto( is_true(clone_el.get("failed", "false")), is_true(clone_el.get("failure_ignored", "false")), target_role, - instance_list, + primitive_list or group_list, ) @@ -270,7 +272,7 @@ def _bundle_to_dto( bundle_el: _Element, _remove_clone_suffix: bool = False ) -> BundleStatusDto: bundle_id = _get_resource_id(bundle_el) - bundle_type = str(bundle_el.get("type")) + bundle_type = str(bundle_el.attrib["type"]) replica_list = [ _replica_to_dto(replica, bundle_id, bundle_type) @@ -283,7 +285,7 @@ def _bundle_to_dto( return BundleStatusDto( bundle_id, bundle_type, - str(bundle_el.get("image")), + str(bundle_el.attrib["image"]), is_true(bundle_el.get("unique", "false")), is_true(bundle_el.get("maintenance", "false")), bundle_el.get("description"), @@ -302,17 +304,18 @@ class ClusterStatusParser: } def __init__(self, status: _Element): - self.status = status - self.warnings: reports.ReportItemList = [] + """ + status -- xml element from crm_mon xml, validated using the appropriate + rng schema + """ + self._status = status + self._warnings: reports.ReportItemList = [] def status_xml_to_dto(self) -> ResourcesStatusDto: """ Return dto containing status of configured resources in the cluster - - status -- status xml document from crm_mon, validated using - the appropriate rng schema """ - resource_list = cast(list[_Element], self.status.xpath("resources/*")) + resource_list = cast(list[_Element], self._status.xpath("resources/*")) resource_dto_list = [] for resource in resource_list: @@ -328,7 +331,7 @@ class ClusterStatusParser: # the implicitly created resource. # We only skip such bundles while still providing status of the # other resources. - self.warnings.append( + self._warnings.append( reports.ReportItem.warning( reports.messages.ClusterStatusBundleMemberIdAsImplicit( e.bundle_id, e.bad_ids @@ -339,11 +342,11 @@ class ClusterStatusParser: return ResourcesStatusDto(resource_dto_list) def get_warnings(self) -> reports.ReportItemList: - return self.warnings + return self._warnings def _get_resource_id(resource: _Element) -> str: - resource_id = resource.get("id") + resource_id = resource.attrib["id"] if not resource_id: raise EmptyResourceIdError() return str(resource_id) @@ -374,7 +377,7 @@ def _remove_clone_suffix(resource_id: str) -> str: def _replica_to_dto( replica_el: _Element, bundle_id: str, bundle_type: str ) -> BundleReplicaStatusDto: - replica_id = str(replica_el.get("id")) + replica_id = str(replica_el.attrib["id"]) resource_list = [ _primitive_to_dto(resource) diff --git a/pcs_test/tier0/common/reports/test_messages.py b/pcs_test/tier0/common/reports/test_messages.py index 48eb730c..0ca95920 100644 --- a/pcs_test/tier0/common/reports/test_messages.py +++ b/pcs_test/tier0/common/reports/test_messages.py @@ -2195,23 +2195,23 @@ class BadClusterStateFormat(NameBuildTest): ) -class BadClusterState(NameBuildTest): +class BadClusterStateData(NameBuildTest): def test_no_reason(self): self.assert_message_from_report( ( "Cannot load cluster status, xml does not describe " - "valid cluster status." + "valid cluster status" ), - reports.BadClusterState(), + reports.BadClusterStateData(), ) def test_reason(self): self.assert_message_from_report( ( "Cannot load cluster status, xml does not describe " - "valid cluster status: sample reason." + "valid cluster status: sample reason" ), - reports.BadClusterState("sample reason"), + reports.BadClusterStateData("sample reason"), ) @@ -5843,7 +5843,7 @@ class ClusterStatusBundleMemberIdAsImplicit(NameBuildTest): self.assert_message_from_report( ( "Skipping bundle 'resource-bundle': resource 'resource' has " - "the same id as some of the implicit bundle resources." + "the same id as some of the implicit bundle resources" ), reports.ClusterStatusBundleMemberIdAsImplicit( "resource-bundle", ["resource"] @@ -5855,7 +5855,7 @@ class ClusterStatusBundleMemberIdAsImplicit(NameBuildTest): ( "Skipping bundle 'resource-bundle': resources 'resource-0', " "'resource-1' have the same id as some of the implicit bundle " - "resources." + "resources" ), reports.ClusterStatusBundleMemberIdAsImplicit( "resource-bundle", ["resource-0", "resource-1"] diff --git a/pcs_test/tier0/lib/commands/test_status.py b/pcs_test/tier0/lib/commands/test_status.py index 3b6b7665..b12e9531 100644 --- a/pcs_test/tier0/lib/commands/test_status.py +++ b/pcs_test/tier0/lib/commands/test_status.py @@ -1342,8 +1342,8 @@ class ResourcesStatus(TestCase): lambda: status.resources_status(self.env_assist.get_env()), [ fixture.error( - report_codes.BAD_CLUSTER_STATE, - reason="Resource with id 'R7' contains unknown pcmk role 'NotPcmkRole'.", + report_codes.BAD_CLUSTER_STATE_DATA, + reason="Resource 'R7' contains an unknown role 'NotPcmkRole'", ), ], False, diff --git a/pcs_test/tier0/lib/pacemaker/test_status.py b/pcs_test/tier0/lib/pacemaker/test_status.py index 778e97a6..ced1a47e 100644 --- a/pcs_test/tier0/lib/pacemaker/test_status.py +++ b/pcs_test/tier0/lib/pacemaker/test_status.py @@ -12,6 +12,7 @@ from pcs.common import reports from pcs.common.const import ( PCMK_ROLE_STARTED, PCMK_ROLES, + PCMK_STATUS_ROLE_PROMOTED, PCMK_STATUS_ROLE_STARTED, PCMK_STATUS_ROLE_STOPPED, PCMK_STATUS_ROLE_UNPROMOTED, @@ -334,8 +335,8 @@ class TestParsingErrorToReport(TestCase): assert_report_item_equal( report, fixture.error( - reports.codes.BAD_CLUSTER_STATE, - reason="Resource with empty id.", + reports.codes.BAD_CLUSTER_STATE_DATA, + reason="Resource with an empty id", ), ) @@ -346,8 +347,8 @@ class TestParsingErrorToReport(TestCase): assert_report_item_equal( report, fixture.error( - reports.codes.BAD_CLUSTER_STATE, - reason="Resource with id 'resource' contains node with empty name.", + reports.codes.BAD_CLUSTER_STATE_DATA, + reason="Resource 'resource' contains a node with an empty name", ), ) @@ -358,25 +359,25 @@ class TestParsingErrorToReport(TestCase): assert_report_item_equal( report, fixture.error( - reports.codes.BAD_CLUSTER_STATE, - reason="Resource with id 'resource' contains unknown pcmk role 'NotPcmkRole'.", + reports.codes.BAD_CLUSTER_STATE_DATA, + reason="Resource 'resource' contains an unknown role 'NotPcmkRole'", ), ) def test_unexpected_member_group(self): report = status.cluster_status_parsing_error_to_report( status.UnexpectedMemberError( - "resource", "group", "member", ["primitive"] + "resource", "group", "member", "bundle", ["primitive"] ) ) assert_report_item_equal( report, fixture.error( - reports.codes.BAD_CLUSTER_STATE, + reports.codes.BAD_CLUSTER_STATE_DATA, reason=( - "Unexpected resource 'member' inside of resource " - "'resource' of type 'group'. Only resources of type " - "'primitive' can be in group." + "Unexpected resource 'member' of type 'bundle' inside of " + "resource 'resource' of type 'group'. Only resources of " + "type 'primitive' can be in a group" ), ), ) @@ -384,17 +385,17 @@ class TestParsingErrorToReport(TestCase): def test_unexpected_member_clone(self): report = status.cluster_status_parsing_error_to_report( status.UnexpectedMemberError( - "resource", "clone", "member", ["primitive", "group"] + "resource", "clone", "member", "bundle", ["primitive", "group"] ) ) assert_report_item_equal( report, fixture.error( - reports.codes.BAD_CLUSTER_STATE, + reports.codes.BAD_CLUSTER_STATE_DATA, reason=( - "Unexpected resource 'member' inside of resource " - "'resource' of type 'clone'. Only resources of type " - "'group'|'primitive' can be in clone." + "Unexpected resource 'member' of type 'bundle' inside of " + "resource 'resource' of type 'clone'. Only resources of " + "type 'group', 'primitive' can be in a clone" ), ), ) @@ -406,8 +407,8 @@ class TestParsingErrorToReport(TestCase): assert_report_item_equal( report, fixture.error( - reports.codes.BAD_CLUSTER_STATE, - reason="Primitive and group members mixed in clone 'resource'.", + reports.codes.BAD_CLUSTER_STATE_DATA, + reason="Primitive and group members mixed in clone 'resource'", ), ) @@ -418,8 +419,8 @@ class TestParsingErrorToReport(TestCase): assert_report_item_equal( report, fixture.error( - reports.codes.BAD_CLUSTER_STATE, - reason="Members with different ids in resource 'resource'.", + reports.codes.BAD_CLUSTER_STATE_DATA, + reason="Members with different ids in clone 'resource'", ), ) @@ -432,8 +433,8 @@ class TestParsingErrorToReport(TestCase): assert_report_item_equal( report, fixture.error( - reports.codes.BAD_CLUSTER_STATE, - reason="Replica '0' of bundle 'resource' is missing implicit container resource.", + reports.codes.BAD_CLUSTER_STATE_DATA, + reason="Replica '0' of bundle 'resource' is missing implicit container resource", ), ) @@ -444,8 +445,8 @@ class TestParsingErrorToReport(TestCase): assert_report_item_equal( report, fixture.error( - reports.codes.BAD_CLUSTER_STATE, - reason="Replica '0' of bundle 'resource' has invalid number of members.", + reports.codes.BAD_CLUSTER_STATE_DATA, + reason="Replica '0' of bundle 'resource' has invalid number of members", ), ) @@ -456,8 +457,8 @@ class TestParsingErrorToReport(TestCase): assert_report_item_equal( report, fixture.error( - reports.codes.BAD_CLUSTER_STATE, - reason="Replicas of bundle 'resource' are not the same.", + reports.codes.BAD_CLUSTER_STATE_DATA, + reason="Replicas of bundle 'resource' are not the same", ), ) @@ -549,6 +550,7 @@ class TestPrimitiveStatusToDto(TestCase): with self.assertRaises(status.UnknownPcmkRoleError) as cm: status._primitive_to_dto(primitive_xml) self.assertEqual(cm.exception.resource_id, "resource") + self.assertEqual(cm.exception.role, "NotPcmkRole") def test_target_role(self): for role in PCMK_ROLES: @@ -573,6 +575,7 @@ class TestPrimitiveStatusToDto(TestCase): with self.assertRaises(status.UnknownPcmkRoleError) as cm: status._primitive_to_dto(primitive_xml) self.assertEqual(cm.exception.resource_id, "resource") + self.assertEqual(cm.exception.role, value) class TestGroupStatusToDto(TestCase): @@ -695,7 +698,11 @@ class TestGroupStatusToDto(TestCase): with self.assertRaises(status.UnexpectedMemberError) as cm: status._group_to_dto(group_xml) self.assertEqual(cm.exception.resource_id, "outer-group") + self.assertEqual(cm.exception.resource_type, "group") self.assertEqual(cm.exception.member_id, resource_id) + self.assertEqual( + cm.exception.member_type, resource_id.split("-")[1] + ) self.assertEqual(cm.exception.expected_types, ["primitive"]) def test_remove_clone_suffix(self): @@ -796,7 +803,7 @@ class TestCloneStatusToDto(TestCase): fixture_clone_xml( multi_state=True, instances=[ - fixture_primitive_xml(role=PCMK_STATUS_ROLE_UNPROMOTED), + fixture_primitive_xml(role=PCMK_STATUS_ROLE_PROMOTED), fixture_primitive_xml( role=PCMK_STATUS_ROLE_UNPROMOTED, node_names=["node2"] ), @@ -810,7 +817,7 @@ class TestCloneStatusToDto(TestCase): fixture_clone_dto( multi_state=True, instances=[ - fixture_primitive_dto(role=PCMK_STATUS_ROLE_UNPROMOTED), + fixture_primitive_dto(role=PCMK_STATUS_ROLE_PROMOTED), fixture_primitive_dto( role=PCMK_STATUS_ROLE_UNPROMOTED, node_names=["node2"] ), @@ -1003,9 +1010,12 @@ class TestCloneStatusToDto(TestCase): with self.assertRaises(status.UnexpectedMemberError) as cm: status._clone_to_dto(clone_xml) - self.assertEqual(cm.exception.resource_id, "outer-clone") + self.assertEqual(cm.exception.resource_type, "clone") self.assertEqual(cm.exception.member_id, resource_id) + self.assertEqual( + cm.exception.member_type, resource_id.split("-")[1] + ) self.assertEqual( cm.exception.expected_types, ["primitive", "group"] ) -- 2.25.1 From c32249a39ef262e3f2106eb8ca01b6efb8e74707 Mon Sep 17 00:00:00 2001 From: Peter Romancik Date: Thu, 1 Feb 2024 17:45:20 +0100 Subject: [PATCH 2/2] store clone instance id in resource status dtos --- pcs/common/status_dto.py | 2 ++ pcs/lib/pacemaker/status.py | 19 +++++++---- pcs_test/tier0/lib/commands/test_status.py | 3 ++ pcs_test/tier0/lib/pacemaker/test_status.py | 36 ++++++++++++++++----- 4 files changed, 46 insertions(+), 14 deletions(-) diff --git a/pcs/common/status_dto.py b/pcs/common/status_dto.py index dcc94eca..240ff930 100644 --- a/pcs/common/status_dto.py +++ b/pcs/common/status_dto.py @@ -16,6 +16,7 @@ from pcs.common.interface.dto import DataTransferObject class PrimitiveStatusDto(DataTransferObject): # pylint: disable=too-many-instance-attributes resource_id: str + clone_instance_id: Optional[str] resource_agent: str role: PcmkStatusRoleType target_role: Optional[PcmkRoleType] @@ -35,6 +36,7 @@ class PrimitiveStatusDto(DataTransferObject): @dataclass(frozen=True) class GroupStatusDto(DataTransferObject): resource_id: str + clone_instance_id: Optional[str] maintenance: bool description: Optional[str] managed: bool diff --git a/pcs/lib/pacemaker/status.py b/pcs/lib/pacemaker/status.py index deb8aa0d..6b37d6cb 100644 --- a/pcs/lib/pacemaker/status.py +++ b/pcs/lib/pacemaker/status.py @@ -152,8 +152,9 @@ def _primitive_to_dto( primitive_el: _Element, remove_clone_suffix: bool = False ) -> PrimitiveStatusDto: resource_id = _get_resource_id(primitive_el) + clone_suffix = None if remove_clone_suffix: - resource_id = _remove_clone_suffix(resource_id) + resource_id, clone_suffix = _remove_clone_suffix(resource_id) role = _get_role(primitive_el) target_role = _get_target_role(primitive_el) @@ -167,6 +168,7 @@ def _primitive_to_dto( return PrimitiveStatusDto( resource_id, + clone_suffix, str(primitive_el.attrib["resource_agent"]), role, target_role, @@ -187,8 +189,11 @@ def _primitive_to_dto( def _group_to_dto( group_el: _Element, remove_clone_suffix: bool = False ) -> GroupStatusDto: - # clone suffix is added even when the clone is non unique - group_id = _remove_clone_suffix(_get_resource_id(group_el)) + # clone instance id present even when the clone is non unique + group_id, clone_instance_id = _remove_clone_suffix( + _get_resource_id(group_el) + ) + member_list = [] for member in group_el: @@ -205,6 +210,7 @@ def _group_to_dto( return GroupStatusDto( group_id, + clone_instance_id, is_true(group_el.get("maintenance", "false")), group_el.get("description"), is_true(group_el.get("managed", "false")), @@ -368,10 +374,11 @@ def _get_target_role(resource: _Element) -> Optional[PcmkRoleType]: return PcmkRoleType(target_role) -def _remove_clone_suffix(resource_id: str) -> str: +def _remove_clone_suffix(resource_id: str) -> tuple[str, Optional[str]]: if ":" in resource_id: - return resource_id.rsplit(":", 1)[0] - return resource_id + resource_id, clone_suffix = resource_id.rsplit(":", 1) + return resource_id, clone_suffix + return resource_id, None def _replica_to_dto( diff --git a/pcs_test/tier0/lib/commands/test_status.py b/pcs_test/tier0/lib/commands/test_status.py index b12e9531..c7c808a3 100644 --- a/pcs_test/tier0/lib/commands/test_status.py +++ b/pcs_test/tier0/lib/commands/test_status.py @@ -1280,6 +1280,7 @@ def _fixture_primitive_resource_dto( ) -> PrimitiveStatusDto: return PrimitiveStatusDto( resource_id=resource_id, + clone_instance_id=None, resource_agent=resource_agent, role=PCMK_STATUS_ROLE_STOPPED, target_role=target_role, @@ -1448,6 +1449,7 @@ class ResourcesStatus(TestCase): ), GroupStatusDto( resource_id="G2", + clone_instance_id=None, maintenance=False, description=None, managed=True, @@ -1475,6 +1477,7 @@ class ResourcesStatus(TestCase): instances=[ GroupStatusDto( resource_id="G1", + clone_instance_id="0", maintenance=False, description=None, managed=True, diff --git a/pcs_test/tier0/lib/pacemaker/test_status.py b/pcs_test/tier0/lib/pacemaker/test_status.py index ced1a47e..a852d45b 100644 --- a/pcs_test/tier0/lib/pacemaker/test_status.py +++ b/pcs_test/tier0/lib/pacemaker/test_status.py @@ -85,6 +85,7 @@ def fixture_primitive_xml( def fixture_primitive_dto( resource_id: str = "resource", + clone_instance_id: Optional[str] = None, resource_agent: str = "ocf:heartbeat:Dummy", role: PcmkStatusRoleType = PCMK_STATUS_ROLE_STARTED, target_role: Optional[str] = None, @@ -94,6 +95,7 @@ def fixture_primitive_dto( ) -> PrimitiveStatusDto: return PrimitiveStatusDto( resource_id, + clone_instance_id, resource_agent, role, target_role, @@ -136,11 +138,13 @@ def fixture_group_xml( def fixture_group_dto( resource_id: str = "resource-group", + clone_instance_id: Optional[str] = None, description: Optional[str] = None, members: Sequence[PrimitiveStatusDto] = (), ) -> GroupStatusDto: return GroupStatusDto( resource_id, + clone_instance_id, maintenance=False, description=description, managed=True, @@ -506,7 +510,7 @@ class TestPrimitiveStatusToDto(TestCase): result = status._primitive_to_dto(primitive_xml, True) - self.assertEqual(result, fixture_primitive_dto()) + self.assertEqual(result, fixture_primitive_dto(clone_instance_id="0")) def test_running_on_multiple_nodes(self): primitive_xml = etree.fromstring( @@ -716,7 +720,10 @@ class TestGroupStatusToDto(TestCase): result = status._group_to_dto(group_xml, True) self.assertEqual( result, - fixture_group_dto(members=[fixture_primitive_dto()]), + fixture_group_dto( + clone_instance_id="0", + members=[fixture_primitive_dto(clone_instance_id="0")], + ), ) @@ -792,8 +799,10 @@ class TestCloneStatusToDto(TestCase): fixture_clone_dto( unique=True, instances=[ - fixture_primitive_dto(), - fixture_primitive_dto(node_names=["node2"]), + fixture_primitive_dto(clone_instance_id="0"), + fixture_primitive_dto( + clone_instance_id="1", node_names=["node2"] + ), ], ), ) @@ -886,9 +895,12 @@ class TestCloneStatusToDto(TestCase): result, fixture_clone_dto( instances=[ - fixture_group_dto(members=[fixture_primitive_dto()]), fixture_group_dto( - members=[fixture_primitive_dto(node_names=["node2"])] + clone_instance_id="0", members=[fixture_primitive_dto()] + ), + fixture_group_dto( + clone_instance_id="1", + members=[fixture_primitive_dto(node_names=["node2"])], ), ], ), @@ -923,9 +935,17 @@ class TestCloneStatusToDto(TestCase): fixture_clone_dto( unique=True, instances=[ - fixture_group_dto(members=[fixture_primitive_dto()]), fixture_group_dto( - members=[fixture_primitive_dto(node_names=["node2"])] + clone_instance_id="0", + members=[fixture_primitive_dto(clone_instance_id="0")], + ), + fixture_group_dto( + clone_instance_id="1", + members=[ + fixture_primitive_dto( + clone_instance_id="1", node_names=["node2"] + ) + ], ), ], ), -- 2.25.1