Skip to content

Commit 0629524

Browse files
committed
Refactor SCS cluster status checker to enhance node attribute processing and improve stability checks
1 parent 818c762 commit 0629524

File tree

1 file changed

+33
-111
lines changed

1 file changed

+33
-111
lines changed

src/modules/get_cluster_status_scs.py

Lines changed: 33 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -2,154 +2,76 @@
22
# Licensed under the MIT License.
33

44
"""
5-
Python script to get and validate the status of a cluster.
6-
This script uses the `crm_mon` command-line tool to retrieve the status of a cluster and performs
7-
various validations on the cluster status.
8-
9-
Methods:
10-
check_node(node, sap_sid)
11-
run_module()
12-
main()
5+
Python script to get and validate the status of an SCS cluster.
136
"""
147

158
import logging
169
import xml.etree.ElementTree as ET
17-
from datetime import datetime
1810
from typing import Dict, Any
1911
from ansible.module_utils.basic import AnsibleModule
2012

2113
try:
22-
from ansible.module_utils.sap_automation_qa import SapAutomationQA, TestStatus
23-
from ansible.module_utils.commands import (
24-
STONITH_ACTION,
25-
PACEMAKER_STATUS,
26-
CLUSTER_STATUS,
27-
)
14+
from ansible.module_utils.get_cluster_status import BaseClusterStatusChecker
2815
except ImportError:
29-
from src.module_utils.sap_automation_qa import SapAutomationQA, TestStatus
30-
from src.module_utils.commands import (
31-
STONITH_ACTION,
32-
PACEMAKER_STATUS,
33-
CLUSTER_STATUS,
34-
)
16+
from src.module_utils.get_cluster_status import BaseClusterStatusChecker
3517

3618

37-
class ClusterStatusChecker(SapAutomationQA):
19+
class SCSClusterStatusChecker(BaseClusterStatusChecker):
3820
"""
39-
Class to check the status of a pacemaker cluster in a SAP HANA environment.
21+
Class to check the status of a pacemaker cluster in an SAP SCS environment.
4022
"""
4123

4224
def __init__(self, sap_sid: str, ansible_os_family: str = ""):
43-
super().__init__()
25+
super().__init__(ansible_os_family)
4426
self.sap_sid = sap_sid
45-
self.ansible_os_family = ansible_os_family
4627
self.result.update(
4728
{
4829
"ascs_node": "",
4930
"ers_node": "",
50-
"cluster_status": "",
51-
"start": datetime.now(),
52-
"end": None,
53-
"pacemaker_status": "",
54-
"stonith_action": "",
5531
}
5632
)
5733

58-
def _get_stonith_action(self) -> None:
59-
"""
60-
Retrieves the STONITH action from the crm_attribute.
61-
"""
62-
try:
63-
stonith_action = self.execute_command_subprocess(STONITH_ACTION[self.ansible_os_family])
64-
stonith_action = (
65-
stonith_action.split("stonith-action:")[-1]
66-
if self.ansible_os_family == "REDHAT"
67-
else stonith_action
68-
)
69-
self.result["stonith_action"] = stonith_action.strip()
70-
except Exception:
71-
self.result["stonith_action"] = "reboot"
72-
7334
def _process_node_attributes(self, node_attributes: ET.Element) -> Dict[str, Any]:
7435
"""
75-
Processes node attributes and identifies primary/secondary nodes.
36+
Processes node attributes and identifies ASCS and ERS nodes.
7637
77-
:param node_attributes: The XML element containing node attributes.
38+
:param node_attributes: XML element containing node attributes.
7839
:type node_attributes: ET.Element
79-
:return: A dictionary containing the primary and secondary node information, plus cluster status.
40+
:return: Dictionary with ASCS and ERS node information.
8041
:rtype: Dict[str, Any]
8142
"""
82-
attribute_name = f"runs_ers_{self.sap_sid.upper()}"
43+
all_nodes = [node.attrib.get("name") for node in node_attributes]
8344
for node in node_attributes:
45+
node_name = node.attrib["name"]
8446
for attribute in node:
85-
if attribute.attrib["name"] == attribute_name:
47+
if attribute.attrib["name"] == f"runs_ers_{self.sap_sid.upper()}":
8648
if attribute.attrib["value"] == "1":
87-
self.result["ers_node"] = node.attrib["name"]
49+
self.result["ers_node"] = node_name
8850
else:
89-
self.result["ascs_node"] = node.attrib["name"]
90-
else:
91-
continue
51+
self.result["ascs_node"] = node_name
52+
53+
if self.result["ascs_node"] == "" and self.result["ers_node"] != "":
54+
self.result["ascs_node"] = next(
55+
(n for n in all_nodes if n != self.result["ers_node"]), ""
56+
)
9257

93-
def run(self) -> Dict[str, str]:
58+
def _is_cluster_ready(self) -> bool:
9459
"""
95-
Main function that runs the Ansible module and performs the cluster status checks.
60+
Check if the cluster is ready by verifying the ASCS node.
9661
97-
This function retrieves operation step from module arguments and performs following checks:
98-
- Checks the status of the cluster using the `crm_mon` command.
99-
- Validates the cluster status and checks if pacemakerd is running.
100-
- Checks if the minimum required number of nodes are configured in the cluster.
101-
- Checks if all nodes in the cluster are online.
102-
- Checks the attributes of each node in the cluster.
62+
:return: True if the cluster is ready, False otherwise.
63+
:rtype: bool
64+
"""
65+
return self.result["ascs_node"] != ""
66+
67+
def _is_cluster_stable(self) -> bool:
68+
"""
69+
Check if the cluster is stable by verifying both ASCS and ERS nodes.
10370
104-
:return: A dictionary containing the result of the cluster status checks.
105-
:rtype: Dict[str, str]
71+
:return: True if the cluster is stable, False otherwise.
72+
:rtype: bool
10673
"""
107-
self.log(logging.INFO, "Starting cluster status check")
108-
109-
self._get_stonith_action()
110-
111-
try:
112-
while self.result["ascs_node"] == "":
113-
self.result["cluster_status"] = self.execute_command_subprocess(CLUSTER_STATUS)
114-
cluster_status_xml = ET.fromstring(self.result["cluster_status"])
115-
self.log(logging.INFO, "Cluster status retrieved")
116-
117-
if self.execute_command_subprocess(PACEMAKER_STATUS).strip() == "active":
118-
self.result["pacemaker_status"] = "running"
119-
else:
120-
self.result["pacemaker_status"] = "stopped"
121-
self.log(logging.INFO, f"Pacemaker status: {self.result['pacemaker_status']}")
122-
123-
if (
124-
int(
125-
cluster_status_xml.find("summary").find("nodes_configured").attrib["number"]
126-
)
127-
< 2
128-
):
129-
self.result["message"] = (
130-
"Pacemaker cluster isn't stable and does not have primary or secondary node"
131-
)
132-
self.log(logging.WARNING, self.result["message"])
133-
134-
nodes = cluster_status_xml.find("nodes")
135-
for node in nodes:
136-
if node.attrib["online"] != "true":
137-
self.result["message"] = f"Node {node.attrib['name']} is not online"
138-
self.log(logging.WARNING, self.result["message"])
139-
140-
self._process_node_attributes(cluster_status_xml.find("node_attributes"))
141-
142-
if self.result["ascs_node"] == "" or self.result["ers_node"] == "":
143-
self.result["message"] = (
144-
"Pacemaker cluster isn't stable and does not have primary or secondary node"
145-
)
146-
self.log(logging.WARNING, self.result["message"])
147-
148-
except Exception as e:
149-
self.handle_error(e)
150-
self.result["end"] = datetime.now()
151-
self.result["status"] = TestStatus.SUCCESS.value
152-
self.log(logging.INFO, "Cluster status check completed")
74+
return self.result["ascs_node"] != "" and self.result["ers_node"] != ""
15375

15476

15577
def run_module() -> None:
@@ -163,7 +85,7 @@ def run_module() -> None:
16385

16486
module = AnsibleModule(argument_spec=module_args, supports_check_mode=True)
16587

166-
checker = ClusterStatusChecker(
88+
checker = SCSClusterStatusChecker(
16789
sap_sid=module.params["sap_sid"],
16890
ansible_os_family=module.params["ansible_os_family"],
16991
)

0 commit comments

Comments
 (0)