Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions src/module_utils/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,21 @@
OperatingSystemFamily.SUSE: ["crm", "configure", "get_property", "stonith-action"],
}

AUTOMATED_REGISTER = [
"cibadmin",
"--query",
"--xpath",
"//nvpair[@name='AUTOMATED_REGISTER']",
AUTOMATED_REGISTER = lambda rsc: [
"crm_resource",
"--resource",
rsc,
"--get-parameter=AUTOMATED_REGISTER",
]

PRIORITY_FENCING_DELAY = [
"crm_attribute",
"--type",
"crm_config",
"--name",
"priority-fencing-delay",
"--quiet",
]

FREEZE_FILESYSTEM = lambda file_system, mount_point: [
"mount",
Expand Down
46 changes: 30 additions & 16 deletions src/modules/get_cluster_status_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@
try:
from ansible.module_utils.get_cluster_status import BaseClusterStatusChecker
from ansible.module_utils.enums import OperatingSystemFamily, HanaSRProvider
from ansible.module_utils.commands import AUTOMATED_REGISTER
from ansible.module_utils.commands import AUTOMATED_REGISTER, PRIORITY_FENCING_DELAY
except ImportError:
from src.module_utils.get_cluster_status import BaseClusterStatusChecker
from src.module_utils.commands import AUTOMATED_REGISTER
from src.module_utils.commands import AUTOMATED_REGISTER, PRIORITY_FENCING_DELAY
from src.module_utils.enums import OperatingSystemFamily, HanaSRProvider


Expand Down Expand Up @@ -146,13 +146,15 @@ def __init__(
db_instance_number: str,
saphanasr_provider: HanaSRProvider,
ansible_os_family: OperatingSystemFamily,
hana_resource_name: str = "",
hana_clone_resource_name: str = "",
hana_primitive_resource_name: str = "",
):
super().__init__(ansible_os_family)
self.database_sid = database_sid
self.saphanasr_provider = saphanasr_provider
self.db_instance_number = db_instance_number
self.hana_resource_name = hana_resource_name
self.hana_clone_resource_name = hana_clone_resource_name
self.hana_primitive_resource_name = hana_primitive_resource_name
self.result.update(
{
"primary_node": "",
Expand All @@ -161,18 +163,28 @@ def __init__(
"replication_mode": "",
"primary_site_name": "",
"AUTOMATED_REGISTER": "false",
"PRIORITY_FENCING_DELAY": "",
}
)

def _get_automation_register(self) -> None:
def _get_cluster_parameters(self) -> None:
"""
Retrieves the value of the AUTOMATED_REGISTER attribute.
Retrieves the values of the AUTOMATED_REGISTER and PRIORITY_FENCING_DELAY attributes.
"""
try:
cmd_output = self.execute_command_subprocess(AUTOMATED_REGISTER).strip()
self.result["AUTOMATED_REGISTER"] = ET.fromstring(cmd_output).get("value")
except Exception:
self.result["AUTOMATED_REGISTER"] = "unknown"
param_commands = {
"AUTOMATED_REGISTER": (
AUTOMATED_REGISTER(self.hana_primitive_resource_name)
if self.hana_primitive_resource_name
else AUTOMATED_REGISTER(self.hana_clone_resource_name)
),
"PRIORITY_FENCING_DELAY": PRIORITY_FENCING_DELAY,
}

for param_name, command in param_commands.items():
try:
self.result[param_name] = self.execute_command_subprocess(command).strip()
except Exception:
self.result[param_name] = "unknown"

def _process_node_attributes(self, cluster_status_xml: ET.Element) -> Dict[str, Any]:
"""
Expand Down Expand Up @@ -209,8 +221,8 @@ def _process_node_attributes(self, cluster_status_xml: ET.Element) -> Dict[str,
HanaSRProvider.ANGI: {
"clone_attr": f"hana_{self.database_sid}_clone_state",
"sync_attr": (
f"master-{self.hana_resource_name}"
if self.hana_resource_name
f"master-{self.hana_clone_resource_name}"
if self.hana_clone_resource_name
else f"master-rsc_SAPHanaCon_{self.database_sid.upper()}"
+ f"_HDB{self.db_instance_number}"
),
Expand Down Expand Up @@ -281,7 +293,7 @@ def run(self) -> Dict[str, str]:
:rtype: Dict[str, str]
"""
result = super().run()
self._get_automation_register()
self._get_cluster_parameters()
return result


Expand All @@ -294,7 +306,8 @@ def run_module() -> None:
database_sid=dict(type="str", required=True),
saphanasr_provider=dict(type="str", required=True),
db_instance_number=dict(type="str", required=True),
hana_resource_name=dict(type="str", required=False),
hana_clone_resource_name=dict(type="str", required=False),
hana_primitive_resource_name=dict(type="str", required=False),
filter=dict(type="str", required=False, default="os_family"),
)

Expand All @@ -307,7 +320,8 @@ def run_module() -> None:
str(ansible_facts(module).get("os_family", "UNKNOWN")).upper()
),
db_instance_number=module.params["db_instance_number"],
hana_resource_name=module.params.get("hana_resource_name", ""),
hana_clone_resource_name=module.params.get("hana_clone_resource_name", ""),
hana_primitive_resource_name=module.params.get("hana_primitive_resource_name", ""),
)
checker.run()

Expand Down
135 changes: 91 additions & 44 deletions src/modules/get_pcmk_properties_scs.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,82 @@ def _validate_resource_constants(self):

return parameters

def _resolve_provider_values(self, expected_value: dict) -> list:
"""
Resolve provider-specific values from a configuration dictionary.

This method handles the complex logic of extracting appropriate values
based on the NFS provider configuration. It supports both provider-specific
configurations and fallback to all available providers.

:param expected_value: Dictionary containing provider configurations
:type expected_value: dict
:return: List of resolved values for validation
:rtype: list
:raises TypeError: If expected_value is not a dictionary
"""
if not isinstance(expected_value, dict):
raise TypeError("Expected value must be a dictionary for provider resolution")

provider_values = []
if self.nfs_provider and self.nfs_provider in expected_value:
provider_config = expected_value[self.nfs_provider]
provider_values = self._extract_values_from_config(provider_config)
else:
for _, provider_config in expected_value.items():
extracted_values = self._extract_values_from_config(provider_config)
if isinstance(extracted_values, list):
provider_values.extend(extracted_values)
else:
provider_values.append(extracted_values)

return provider_values if isinstance(provider_values, list) else [provider_values]

def _extract_values_from_config(self, provider_config):
"""
Extract values from a provider configuration structure.

Handles various configuration formats:
- {"value": [list]} or {"value": "single"}
- [list] directly
- "single" value directly

:param provider_config: Configuration object to extract values from
:type provider_config: dict or list or str
:return: Extracted value(s)
:rtype: list or str
"""
if isinstance(provider_config, dict) and "value" in provider_config:
return provider_config["value"]
elif isinstance(provider_config, (list, str)):
return provider_config
else:
return provider_config

def _compare_value_with_expectations(self, value: str, expected_values) -> str:
"""
Compare a value against expected values and return test status.

:param value: The actual value to compare
:type value: str
:param expected_values: Expected value(s) for comparison
:type expected_values: str or list
:return: Test status (SUCCESS or ERROR)
:rtype: str
"""
if isinstance(expected_values, list):
return (
TestStatus.SUCCESS.value
if str(value) in [str(v) for v in expected_values]
else TestStatus.ERROR.value
)
else:
return (
TestStatus.SUCCESS.value
if str(value) == str(expected_values)
else TestStatus.ERROR.value
)

def _determine_parameter_status(self, value, expected_value):
"""
Determine the status of a parameter with SCS-specific logic for NFS provider.
Expand All @@ -257,60 +333,31 @@ def _determine_parameter_status(self, value, expected_value):
:return: The status of the parameter.
:rtype: str
"""
# Handle tuple format (value, required)
if isinstance(expected_value, tuple):
expected_val, required = expected_value
if not required and (expected_val is None or value == ""):
return TestStatus.INFO.value
expected_value = expected_val

# Handle empty/null cases
if expected_value is None or value == "":
return TestStatus.INFO.value

# Handle simple string/list cases
elif isinstance(expected_value, (str, list)):
if isinstance(expected_value, list):
return (
TestStatus.SUCCESS.value
if str(value) in expected_value
else TestStatus.ERROR.value
)
else:
return (
TestStatus.SUCCESS.value
if str(value) == str(expected_value)
else TestStatus.ERROR.value
)
return self._compare_value_with_expectations(value, expected_value)

# Handle complex provider-based dictionary cases
elif isinstance(expected_value, dict):
provider_values = []
if self.nfs_provider and self.nfs_provider in expected_value:
provider_config = expected_value[self.nfs_provider]
if isinstance(provider_config, dict) and "value" in provider_config:
provider_values = provider_config["value"]
else:
provider_values = provider_config
else:
# If provider is unknown/not set, collect all provider values
for provider_key, provider_config in expected_value.items():
if isinstance(provider_config, dict) and "value" in provider_config:
if isinstance(provider_config["value"], list):
provider_values.extend(provider_config["value"])
else:
provider_values.append(provider_config["value"])
elif isinstance(provider_config, list):
provider_values.extend(provider_config)
else:
provider_values.append(provider_config)

if isinstance(provider_values, list):
return (
TestStatus.SUCCESS.value
if str(value) in provider_values
else TestStatus.ERROR.value
)
else:
return (
TestStatus.SUCCESS.value
if str(value) == str(provider_values)
else TestStatus.ERROR.value
)
try:
provider_values = self._resolve_provider_values(expected_value)
return self._compare_value_with_expectations(value, provider_values)
except (TypeError, KeyError) as ex:
self.result["message"] += f"Error resolving provider values: {str(ex)} "
return TestStatus.ERROR.value

# Handle unexpected types
else:
return TestStatus.ERROR.value

Expand Down
16 changes: 12 additions & 4 deletions src/roles/ha_db_hana/tasks/block-network.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
- node_tier == "hana"
- pre_validations_status == "PASSED"
- cluster_status_pre.stonith_action == "reboot"
- cluster_status_pre.PRIORITY_FENCING_DELAY is defined
- cluster_status_pre.PRIORITY_FENCING_DELAY != ""
- cluster_status_pre.PRIORITY_FENCING_DELAY != "unknown"
block:
- name: "Test Execution: Block Network Communication"
when: ansible_hostname == cluster_status_pre.primary_node
Expand Down Expand Up @@ -93,7 +96,8 @@
operation_step: "test_execution"
database_sid: "{{ db_sid | lower }}"
saphanasr_provider: "{{ saphanasr_provider | default('SAPHanaSR') }}"
hana_resource_name: "{{ hana_resource_name | default('') }}"
hana_clone_resource_name: "{{ hana_clone_resource_name | default('') }}"
hana_primitive_resource_name: "{{ hana_primitive_resource_name | default('') }}"
register: cluster_status_test_execution_primary
retries: "{{ default_retries }}"
delay: "{{ default_delay }}"
Expand All @@ -120,7 +124,8 @@
operation_step: "test_execution"
database_sid: "{{ db_sid | lower }}"
saphanasr_provider: "{{ saphanasr_provider | default('SAPHanaSR') }}"
hana_resource_name: "{{ hana_resource_name | default('') }}"
hana_clone_resource_name: "{{ hana_clone_resource_name | default('') }}"
hana_primitive_resource_name: "{{ hana_primitive_resource_name | default('') }}"
register: cluster_status_post_primary
retries: "{{ default_retries }}"
delay: "{{ default_delay }}"
Expand All @@ -140,7 +145,8 @@
operation_step: "test_execution"
database_sid: "{{ db_sid | lower }}"
saphanasr_provider: "{{ saphanasr_provider | default('SAPHanaSR') }}"
hana_resource_name: "{{ hana_resource_name | default('') }}"
hana_clone_resource_name: "{{ hana_clone_resource_name | default('') }}"
hana_primitive_resource_name: "{{ hana_primitive_resource_name | default('') }}"
register: cluster_status_test_execution_secondary
retries: "{{ default_retries }}"
delay: "{{ default_delay }}"
Expand All @@ -158,7 +164,8 @@
operation_step: "test_execution"
database_sid: "{{ db_sid | lower }}"
saphanasr_provider: "{{ saphanasr_provider | default('SAPHanaSR') }}"
hana_resource_name: "{{ hana_resource_name | default('') }}"
hana_clone_resource_name: "{{ hana_clone_resource_name | default('') }}"
hana_primitive_resource_name: "{{ hana_primitive_resource_name | default('') }}"
register: cluster_status_post_secondary
retries: "{{ default_retries }}"
delay: "{{ default_delay }}"
Expand Down Expand Up @@ -227,6 +234,7 @@
test_case_details_from_test_case: {
"Pre Validations: Remove any location_constraints": "{{ location_constraints_results }}",
"Pre Validations: Validate HANA DB cluster status": "{{ cluster_status_pre }}",
"Pre Validations: priority-fencing-delay": "{{ cluster_status_pre.PRIORITY_FENCING_DELAY | default('Not Configured') }}",
"Pre Validations: CleanUp any failed resource": "{{ cleanup_failed_resource_pre }}",
"Cluster Status": "{{ cluster_status_pre }}",
}
Expand Down
1 change: 0 additions & 1 deletion src/roles/ha_db_hana/tasks/files/constants.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -731,7 +731,6 @@ OS_PARAMETERS:
# === Global INI ===
# Reading the global.ini file to get the provider and path for the SAPHanaSR resource agent
GLOBAL_INI:
GLOBAL_INI:
SUSE:
SAPHanaSR:
provider:
Expand Down
6 changes: 4 additions & 2 deletions src/roles/ha_db_hana/tasks/fs-freeze.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@
operation_step: "test_execution"
database_sid: "{{ db_sid | lower }}"
saphanasr_provider: "{{ saphanasr_provider | default('SAPHanaSR') }}"
hana_resource_name: "{{ hana_resource_name | default('') }}"
hana_clone_resource_name: "{{ hana_clone_resource_name | default('') }}"
hana_primitive_resource_name: "{{ hana_primitive_resource_name | default('') }}"
register: cluster_status_test_execution
retries: "{{ default_retries }}"
delay: "{{ default_delay }}"
Expand All @@ -76,7 +77,8 @@
operation_step: "post_failover"
database_sid: "{{ db_sid | lower }}"
saphanasr_provider: "{{ saphanasr_provider | default('SAPHanaSR') }}"
hana_resource_name: "{{ hana_resource_name | default('') }}"
hana_clone_resource_name: "{{ hana_clone_resource_name | default('') }}"
hana_primitive_resource_name: "{{ hana_primitive_resource_name | default('') }}"
register: cluster_status_post
retries: "{{ default_retries }}"
delay: "{{ default_delay }}"
Expand Down
Loading