Skip to content

Commit efc3d55

Browse files
committed
split QuorumStatus to a pure dataclass and a facade
1 parent 2472010 commit efc3d55

File tree

5 files changed

+538
-457
lines changed

5 files changed

+538
-457
lines changed

pcs/cluster.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,11 @@
6060
from pcs.lib.communication.tools import RunRemotelyBase
6161
from pcs.lib.communication.tools import run as run_com_cmd
6262
from pcs.lib.communication.tools import run_and_raise
63-
from pcs.lib.corosync import live as corosync_live
6463
from pcs.lib.corosync import qdevice_net
64+
from pcs.lib.corosync.live import (
65+
QuorumStatusException,
66+
QuorumStatusFacade,
67+
)
6568
from pcs.lib.errors import LibraryError
6669
from pcs.lib.node import get_existing_nodes_names
6770
from pcs.utils import parallel_for_nodes
@@ -495,14 +498,14 @@ def stop_cluster_nodes(nodes):
495498
error_list.append(node + ": " + data)
496499
continue
497500
try:
498-
quorum_status = corosync_live.parse_quorum_status(data)
499-
if not quorum_status.is_quorate:
501+
quorum_status_facade = QuorumStatusFacade.from_string(data)
502+
if not quorum_status_facade.is_quorate:
500503
# Get quorum status from a quorate node, non-quorate nodes
501504
# may provide inaccurate info. If no node is quorate, there
502505
# is no quorum to be lost and therefore no error to be
503506
# reported.
504507
continue
505-
if quorum_status.stopping_nodes_cause_quorum_loss(nodes):
508+
if quorum_status_facade.stopping_nodes_cause_quorum_loss(nodes):
506509
utils.err(
507510
"Stopping the node(s) will cause a loss of the quorum"
508511
+ ", use --force to override"
@@ -511,7 +514,7 @@ def stop_cluster_nodes(nodes):
511514
# We have the info, no need to print errors
512515
error_list = []
513516
break
514-
except corosync_live.QuorumStatusException:
517+
except QuorumStatusException:
515518
if not utils.is_node_offline_by_quorumtool_output(data):
516519
error_list.append(node + ": Unable to get quorum status")
517520
# else the node seems to be stopped already
@@ -688,14 +691,14 @@ def stop_cluster(argv):
688691
# - retval is 2 on success if a node is not in a partition with quorum
689692
output, dummy_retval = utils.run(["corosync-quorumtool", "-p", "-s"])
690693
try:
691-
if corosync_live.parse_quorum_status(
694+
if QuorumStatusFacade.from_string(
692695
output
693696
).stopping_local_node_cause_quorum_loss():
694697
utils.err(
695698
"Stopping the node will cause a loss of the quorum"
696699
+ ", use --force to override"
697700
)
698-
except corosync_live.QuorumStatusException:
701+
except QuorumStatusException:
699702
if not utils.is_node_offline_by_quorumtool_output(output):
700703
utils.err(
701704
"Unable to determine whether stopping the node will cause "

pcs/lib/commands/cluster.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1851,9 +1851,11 @@ def remove_nodes(
18511851
# required, cluster has to be turned off and therefore it loses quorum.
18521852
com_cmd = cluster.GetQuorumStatus(report_processor)
18531853
com_cmd.set_targets(targets_to_remove)
1854-
failures, quorum_status = run_com(env.get_node_communicator(), com_cmd)
1855-
if quorum_status:
1856-
if quorum_status.stopping_nodes_cause_quorum_loss(node_list):
1854+
failures, quorum_status_facade = run_com(
1855+
env.get_node_communicator(), com_cmd
1856+
)
1857+
if quorum_status_facade:
1858+
if quorum_status_facade.stopping_nodes_cause_quorum_loss(node_list):
18571859
report_processor.report(
18581860
ReportItem(
18591861
severity=reports.item.get_severity(

pcs/lib/communication/cluster.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Optional
2+
13
from pcs.common import reports
24
from pcs.common.node_communicator import RequestData
35
from pcs.common.reports.item import ReportItem
@@ -9,7 +11,10 @@
911
SimpleResponseProcessingMixin,
1012
SkipOfflineMixin,
1113
)
12-
from pcs.lib.corosync import live as corosync_live
14+
from pcs.lib.corosync.live import (
15+
QuorumStatusException,
16+
QuorumStatusFacade,
17+
)
1318
from pcs.lib.node_communication import response_to_report_item
1419

1520

@@ -84,8 +89,8 @@ def on_complete(self):
8489

8590

8691
class GetQuorumStatus(AllSameDataMixin, OneByOneStrategyMixin, RunRemotelyBase):
87-
_quorum_status = None
88-
_has_failure = False
92+
_quorum_status_facade: Optional[QuorumStatusFacade] = None
93+
_has_failure: Optional[bool] = False
8994

9095
def _get_request_data(self):
9196
return RequestData("remote/get_quorum_info")
@@ -103,11 +108,11 @@ def _process_response(self, response):
103108
# corosync is not running on the node, this is OK
104109
return self._get_next_list()
105110
try:
106-
quorum_status = corosync_live.parse_quorum_status(response.data)
107-
if not quorum_status.is_quorate:
111+
quorum_status_facade = QuorumStatusFacade.from_string(response.data)
112+
if not quorum_status_facade.is_quorate:
108113
return self._get_next_list()
109-
self._quorum_status = quorum_status
110-
except corosync_live.QuorumStatusParsingException as e:
114+
self._quorum_status_facade = quorum_status_facade
115+
except QuorumStatusException as e:
111116
self._has_failure = True
112117
self._report(
113118
ReportItem.warning(
@@ -120,5 +125,7 @@ def _process_response(self, response):
120125
return self._get_next_list()
121126
return []
122127

123-
def on_complete(self):
124-
return self._has_failure, self._quorum_status
128+
def on_complete(
129+
self,
130+
) -> tuple[Optional[bool], Optional[QuorumStatusFacade]]:
131+
return self._has_failure, self._quorum_status_facade

pcs/lib/corosync/live.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,35 @@ def qdevice_votes(self) -> int:
100100
"""
101101
return sum(qdevice.votes for qdevice in self.qdevice_list)
102102

103+
104+
class QuorumStatusFacade:
105+
@classmethod
106+
def from_string(cls, quorum_status_string: str) -> "QuorumStatusFacade":
107+
return cls(_parse_quorum_status(quorum_status_string))
108+
109+
def __init__(self, quorum_status: QuorumStatus):
110+
self._quorum_status = quorum_status
111+
112+
@property
113+
def node_list(self) -> Sequence[QuorumStatusNode]:
114+
return self._quorum_status.node_list
115+
116+
@property
117+
def qdevice_list(self) -> Sequence[QuorumStatusNode]:
118+
return self._quorum_status.qdevice_list
119+
120+
@property
121+
def is_quorate(self) -> bool:
122+
return self._quorum_status.is_quorate
123+
124+
@property
125+
def votes_needed_for_quorum(self) -> int:
126+
return self._quorum_status.votes_needed_for_quorum
127+
128+
@property
129+
def qdevice_votes(self) -> int:
130+
return self._quorum_status.qdevice_votes
131+
103132
def _get_votes_excluding_nodes(self, node_names: Container[str]) -> int:
104133
"""
105134
How many votes do remain if specified nodes are not counted in?
@@ -151,7 +180,7 @@ def stopping_local_node_cause_quorum_loss(self) -> bool:
151180
)
152181

153182

154-
def parse_quorum_status(quorum_status: str) -> QuorumStatus:
183+
def _parse_quorum_status(quorum_status: str) -> QuorumStatus:
155184
# pylint: disable=too-many-branches
156185
node_list: list[QuorumStatusNode] = []
157186
qdevice_list: list[QuorumStatusNode] = []

0 commit comments

Comments
 (0)