diff --git a/.gitignore b/.gitignore
index 82d7c8f1..1bd7e322 100644
--- a/.gitignore
+++ b/.gitignore
@@ -409,3 +409,6 @@ VMWPASSWORD
.coverage.*
*.ini
.ansible/
+
+# GitHub Copilot configuration files
+copilot-instructions.md
diff --git a/requirements.txt b/requirements.txt
index 3a5f1874..bd3a4766 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,14 +4,14 @@
#
# pip-compile requirements.in
#
-ansible-compat==25.6.0
+ansible-compat==25.8.1
# via ansible-lint
-ansible-core==2.17.13
+ansible-core==2.17.14
# via
# -r requirements.in
# ansible-compat
# ansible-lint
-ansible-lint==25.6.1
+ansible-lint==25.8.2
# via -r requirements.in
ansible-runner==2.4.1
# via -r requirements.in
@@ -30,7 +30,7 @@ azure-core==1.35.0
# azure-mgmt-core
# azure-storage-blob
# azure-storage-queue
-azure-identity==1.23.1
+azure-identity==1.24.0
# via
# -r requirements.in
# azure-kusto-data
@@ -58,21 +58,21 @@ black==25.1.0
# ansible-lint
bracex==2.6
# via wcmatch
-certifi==2025.7.14
+certifi==2025.8.3
# via requests
-cffi==1.17.1
+cffi==2.0.0
# via cryptography
-charset-normalizer==3.4.2
+charset-normalizer==3.4.3
# via requests
click==8.2.1
# via
# -r requirements.in
# black
-coverage[toml]==7.10.0
+coverage[toml]==7.10.6
# via
# -r requirements.in
# pytest-cov
-cryptography==45.0.5
+cryptography==45.0.7
# via
# ansible-core
# azure-identity
@@ -84,7 +84,7 @@ dill==0.4.0
# via pylint
exceptiongroup==1.3.0
# via pytest
-filelock==3.18.0
+filelock==3.19.1
# via ansible-lint
idna==3.10
# via requests
@@ -107,15 +107,15 @@ jinja2==3.1.6
# ansible-core
jmespath==1.0.1
# via -r requirements.in
-jsonschema==4.25.0
+jsonschema==4.25.1
# via
# ansible-compat
# ansible-lint
-jsonschema-specifications==2025.4.1
+jsonschema-specifications==2025.9.1
# via jsonschema
lockfile==0.12.2
# via python-daemon
-markdown-it-py==3.0.0
+markdown-it-py==4.0.0
# via rich
markupsafe==3.0.2
# via jinja2
@@ -144,7 +144,7 @@ packaging==25.0
# ansible-runner
# black
# pytest
-pandas==2.3.1
+pandas==2.3.2
# via -r requirements.in
pathspec==0.12.1
# via
@@ -153,7 +153,7 @@ pathspec==0.12.1
# yamllint
pexpect==4.9.0
# via ansible-runner
-platformdirs==4.3.8
+platformdirs==4.4.0
# via
# black
# pylint
@@ -163,7 +163,7 @@ pluggy==1.6.0
# pytest-cov
ptyprocess==0.7.0
# via pexpect
-pycparser==2.22
+pycparser==2.23
# via cffi
pygments==2.19.2
# via
@@ -173,16 +173,16 @@ pyjwt[crypto]==2.10.1
# via
# msal
# pyjwt
-pylint==3.3.7
+pylint==3.3.8
# via -r requirements.in
-pytest==8.4.1
+pytest==8.4.2
# via
# -r requirements.in
# pytest-cov
# pytest-mock
-pytest-cov==6.2.1
+pytest-cov==7.0.0
# via -r requirements.in
-pytest-mock==3.14.1
+pytest-mock==3.15.0
# via -r requirements.in
python-daemon==3.1.2
# via ansible-runner
@@ -205,7 +205,7 @@ referencing==0.36.2
# ansible-lint
# jsonschema
# jsonschema-specifications
-requests==2.32.4
+requests==2.32.5
# via
# -r requirements.in
# azure-core
@@ -213,13 +213,13 @@ requests==2.32.4
# msal
resolvelib==1.0.1
# via ansible-core
-rich==14.0.0
+rich==14.1.0
# via -r requirements.in
-rpds-py==0.26.0
+rpds-py==0.27.1
# via
# jsonschema
# referencing
-ruamel-yaml==0.18.14
+ruamel-yaml==0.18.15
# via ansible-lint
ruamel-yaml-clib==0.2.12
# via ruamel-yaml
@@ -241,7 +241,7 @@ tomli==2.2.1
# pytest
tomlkit==0.13.3
# via pylint
-typing-extensions==4.14.1
+typing-extensions==4.15.0
# via
# astroid
# azure-core
@@ -252,7 +252,6 @@ typing-extensions==4.14.1
# black
# exceptiongroup
# referencing
- # rich
tzdata==2025.2
# via pandas
urllib3==2.5.0
diff --git a/scripts/setup.sh b/scripts/setup.sh
index 47b6a249..4abc0d49 100755
--- a/scripts/setup.sh
+++ b/scripts/setup.sh
@@ -13,9 +13,21 @@ set_output_context
# Ensure we're in the project root directory
cd "$(dirname "$script_dir")"
-packages=("python3-pip" "ansible" "sshpass" "python3-venv")
+packages=("python3-pip" "sshpass" "python3-venv")
install_packages "${packages[@]}"
+# Install az cli if not present
+if ! command_exists az; then
+ log "INFO" "Azure CLI not found. Installing Azure CLI..."
+ curl -L https://aka.ms/InstallAzureCli | bash
+ if command_exists az; then
+ log "INFO" "Azure CLI installed successfully."
+ else
+ log "ERROR" "Failed to install Azure CLI. Please install it manually."
+ exit 1
+ fi
+fi
+
# Verify Python3 is available
if ! command_exists python3; then
log "ERROR" "Python3 is not available after installation. Please install Python3 manually."
@@ -47,7 +59,7 @@ log "INFO" "Installing Python packages..."
if ! pip install --upgrade pip; then
log "ERROR" "Failed to upgrade pip."
fi
-if pip install pyyaml requests azure-identity azure-kusto-data azure-kusto-ingest azure-mgmt-network azure-storage-blob azure-storage-queue; then
+if pip install -r requirements.in; then
log "INFO" "Python packages installed successfully."
else
log "ERROR" "Failed to install Python packages."
diff --git a/src/ansible.cfg b/src/ansible.cfg
index 28b92a00..5c7636f6 100644
--- a/src/ansible.cfg
+++ b/src/ansible.cfg
@@ -9,7 +9,7 @@ display_skipped_hosts = False
conditional_bare_variables = False
interpreter_python = auto_silent
callbacks_enabled = profile_tasks
-stdout_callback = yaml
+stdout_callback = default
bin_ansible_callbacks = True
host_key_checking = False
error_on_undefined_vars = True
diff --git a/src/module_utils/get_pcmk_properties.py b/src/module_utils/get_pcmk_properties.py
index dc796165..322bc12d 100644
--- a/src/module_utils/get_pcmk_properties.py
+++ b/src/module_utils/get_pcmk_properties.py
@@ -89,14 +89,35 @@ def _get_expected_value(self, category, name):
:param name: The name of the configuration parameter.
:type name: str
:return: The expected value for the configuration parameter.
- :rtype: str
+ :rtype: tuple(str, bool)
"""
_, defaults_key = self.BASIC_CATEGORIES[category]
fence_config = self.constants["VALID_CONFIGS"].get(self.fencing_mechanism, {})
os_config = self.constants["VALID_CONFIGS"].get(self.os_type, {})
- return fence_config.get(name) or os_config.get(name, self.constants[defaults_key].get(name))
+ fence_param = fence_config.get(name, {})
+ if fence_param:
+ if isinstance(fence_param, dict) and fence_param.get("value"):
+ return (fence_param.get("value", ""), fence_param.get("required", False))
+ elif isinstance(fence_param, (str, list)):
+ return (fence_param, False)
+
+ os_param = os_config.get(name, {})
+ if os_param:
+ if isinstance(os_param, dict) and os_param.get("value"):
+ return (os_param.get("value", ""), os_param.get("required", False))
+ elif isinstance(os_param, (str, list)):
+ return (os_param, False)
+
+ default_param = self.constants[defaults_key].get(name, {})
+ if default_param:
+ if isinstance(default_param, dict) and default_param.get("value"):
+ return (default_param.get("value", ""), default_param.get("required", False))
+ elif isinstance(default_param, (str, list)):
+ return (default_param, False)
+
+ return None
def _get_resource_expected_value(self, resource_type, section, param_name, op_name=None):
"""
@@ -111,20 +132,21 @@ def _get_resource_expected_value(self, resource_type, section, param_name, op_na
:param op_name: The name of the operation (if applicable), defaults to None
:type op_name: str, optional
:return: The expected value for the resource configuration parameter.
- :rtype: str
+ :rtype: tuple(str, bool)
"""
resource_defaults = (
self.constants["RESOURCE_DEFAULTS"].get(self.os_type, {}).get(resource_type, {})
)
-
+ attr = None
if section == "meta_attributes":
- return resource_defaults.get("meta_attributes", {}).get(param_name)
+ attr = resource_defaults.get("meta_attributes", {}).get(param_name)
elif section == "operations":
ops = resource_defaults.get("operations", {}).get(op_name, {})
- return ops.get(param_name)
+ attr = ops.get(param_name)
elif section == "instance_attributes":
- return resource_defaults.get("instance_attributes", {}).get(param_name)
- return None
+ attr = resource_defaults.get("instance_attributes", {}).get(param_name)
+
+ return (attr.get("value"), attr.get("required", False)) if attr else None
def _create_parameter(
self,
@@ -157,22 +179,36 @@ def _create_parameter(
:rtype: dict
"""
if expected_value is None:
- expected_value = self._get_expected_value_for_category(
+ expected_config = self._get_expected_value_for_category(
category, subcategory, name, op_name
)
+ else:
+ if isinstance(expected_value, tuple) and len(expected_value) == 2:
+ expected_config = expected_value # Already in correct format
+ else:
+ expected_config = (expected_value, False)
- status = self._determine_parameter_status(value, expected_value)
+ status = self._determine_parameter_status(value, expected_config)
- if isinstance(expected_value, list):
- expected_value = expected_value[0] if expected_value else ""
- elif isinstance(expected_value, dict):
- expected_value = (
+ display_expected_value = None
+ if expected_config is None:
+ display_expected_value = ""
+ else:
+ if isinstance(expected_config, tuple):
+ display_expected_value = expected_config[0]
+ else:
+ display_expected_value = expected_config
+
+ if isinstance(display_expected_value, list):
+ display_expected_value = display_expected_value[0] if display_expected_value else ""
+ elif isinstance(display_expected_value, dict):
+ display_expected_value = (
[
item
- for val in expected_value.values()
+ for val in display_expected_value.values()
for item in (val if isinstance(val, list) else [val])
]
- if expected_value
+ if display_expected_value
else ""
)
@@ -181,7 +217,7 @@ def _create_parameter(
id=id if id else "",
name=name if not op_name else f"{op_name}_{name}",
value=value,
- expected_value=expected_value if expected_value is not None else "",
+ expected_value=display_expected_value if display_expected_value is not None else "",
status=status if status else TestStatus.ERROR.value,
).to_dict()
@@ -211,34 +247,47 @@ def _get_expected_value_for_category(self, category, subcategory, name, op_name)
else:
return self._get_expected_value(category, name)
- def _determine_parameter_status(self, value, expected_value):
+ def _determine_parameter_status(self, value, expected_config):
"""
Determine the status of a parameter based on its value and expected value.
:param value: The actual value of the parameter.
:type value: str
- :param expected_value: The expected value of the parameter.
- :type expected_value: str or list or dict
+ :param expected_config: The expected value of the parameter and bool indicating if required.
+ :type expected_config: tuple(str, bool)
:return: The status of the parameter.
:rtype: str
"""
- if expected_value is None or value == "":
+ if expected_config is None:
return TestStatus.INFO.value
- 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
- )
+
+ if isinstance(expected_config, tuple):
+ expected_value, is_required = expected_config
+ elif isinstance(expected_config, dict):
+ expected_value = expected_config.get("value")
+ is_required = expected_config.get("required", False)
+ else:
+ expected_value = expected_config
+ is_required = False
+
+ if not value or value == "":
+ if is_required:
+ return TestStatus.WARNING.value
else:
- return (
- TestStatus.SUCCESS.value
- if str(value) == str(expected_value)
- else TestStatus.ERROR.value
- )
+ return TestStatus.INFO.value
+
+ if expected_value is None or expected_value == "":
+ return TestStatus.INFO.value
+ elif isinstance(expected_value, list):
+ return (
+ TestStatus.SUCCESS.value if str(value) in expected_value else TestStatus.ERROR.value
+ )
else:
- return TestStatus.ERROR.value
+ return (
+ TestStatus.SUCCESS.value
+ if str(value) == str(expected_value)
+ else TestStatus.ERROR.value
+ )
def _parse_nvpair_elements(self, elements, category, subcategory=None, op_name=None):
"""
@@ -297,38 +346,12 @@ def _parse_os_parameters(self):
id=section,
name=param_name,
value=value,
- expected_value=expected_value,
+ expected_value=expected_value.get("value", "") if expected_value else None,
)
)
return parameters
- def _parse_basic_config(self, element, category, subcategory=None):
- """
- Parse basic configuration parameters
-
- :param element: The XML element to parse.
- :type element: xml.etree.ElementTree.Element
- :param category: The category of the configuration parameter.
- :type category: str
- :param subcategory: The subcategory of the configuration parameter, defaults to None
- :type subcategory: str, optional
- :return: A list of parameter dictionaries.
- :rtype: list
- """
- parameters = []
- for nvpair in element.findall(".//nvpair"):
- parameters.append(
- self._create_parameter(
- category=category,
- subcategory=subcategory,
- name=nvpair.get("name", ""),
- value=nvpair.get("value", ""),
- id=nvpair.get("id", ""),
- )
- )
- return parameters
-
def _parse_resource(self, element, category):
"""
Parse resource-specific configuration parameters
@@ -376,37 +399,6 @@ def _parse_resource(self, element, category):
)
return parameters
- def _parse_constraints(self, root):
- """
- Parse constraints configuration parameters
-
- :param root: The XML root element to parse.
- :type root: xml.etree.ElementTree.Element
- :return: A list of parameter dictionaries.
- :rtype: list
- """
- parameters = []
- for element in root:
- tag = element.tag
- if tag in self.constants["CONSTRAINTS"]:
- for attr, expected in self.constants["CONSTRAINTS"][tag].items():
- if element.get(attr) is not None:
- parameters.append(
- self._create_parameter(
- category="constraints",
- subcategory=tag,
- id=element.get("id", ""),
- name=attr,
- value=element.get(attr),
- expected_value=expected,
- )
- )
- else:
- continue
- else:
- continue
- return parameters
-
def _parse_resources_section(self, root):
"""
Parse resources section - can be overridden by subclasses for custom resource parsing.
@@ -476,57 +468,26 @@ def _get_scope_from_cib(self, scope):
return self.cib_output.find(xpath)
return None
- def parse_ha_cluster_config(self):
+ def validate_from_constants(self):
"""
- Parse HA cluster configuration XML and return a list of properties.
- This is the main orchestration method that coordinates all parsing activities.
+ Constants-first validation approach: iterate through constants and validate against CIB.
+ This ensures all expected parameters are checked, with offline validation support.
"""
parameters = []
- scopes = [
- "rsc_defaults",
- "crm_config",
- "op_defaults",
- "constraints",
- "resources",
- ]
-
- for scope in scopes:
- if self._should_skip_scope(scope):
- continue
-
- self.category = scope
- if self.cib_output:
- root = self._get_scope_from_cib(scope)
- else:
- root = self.parse_xml_output(
- self.execute_command_subprocess(CIB_ADMIN(scope=scope))
- )
- if not root:
- continue
-
- try:
- if self.category in self.BASIC_CATEGORIES:
- xpath = self.BASIC_CATEGORIES[self.category][0]
- for element in root.findall(xpath):
- parameters.extend(self._parse_basic_config(element, self.category))
-
- elif self.category == "resources":
- parameters.extend(self._parse_resources_section(root))
-
- elif self.category == "constraints":
- parameters.extend(self._parse_constraints(root))
-
- except Exception as ex:
- self.result["message"] += f"Failed to get {self.category} configuration: {str(ex)}"
- continue
+ for category in ["crm_config", "rsc_defaults", "op_defaults"]:
+ if not self._should_skip_scope(category):
+ parameters.extend(self._validate_basic_constants(category))
+ parameters.extend(self._validate_resource_constants())
+ parameters.extend(self._validate_constraint_constants())
try:
if not self.cib_output:
parameters.extend(self._parse_os_parameters())
else:
self.result["message"] += "CIB output provided, skipping OS parameters parsing. "
except Exception as ex:
- self.result["message"] += f"Failed to get OS parameters: {str(ex)} \n"
+ self.result["message"] += f"Failed to get OS parameters: {str(ex)} "
+
try:
if not self.cib_output:
parameters.extend(self._get_additional_parameters())
@@ -535,18 +496,174 @@ def parse_ha_cluster_config(self):
"message"
] += "CIB output provided, skipping additional parameters parsing. "
except Exception as ex:
- self.result["message"] += f"Failed to get additional parameters: {str(ex)} \n"
+ self.result["message"] += f"Failed to get additional parameters: {str(ex)} "
+
failed_parameters = [
param
for param in parameters
if param.get("status", TestStatus.ERROR.value) == TestStatus.ERROR.value
]
+ warning_parameters = [
+ param for param in parameters if param.get("status", "") == TestStatus.WARNING.value
+ ]
+
+ if failed_parameters:
+ overall_status = TestStatus.ERROR.value
+ elif warning_parameters:
+ overall_status = TestStatus.WARNING.value
+ else:
+ overall_status = TestStatus.SUCCESS.value
+
self.result.update(
{
"details": {"parameters": parameters},
- "status": (
- TestStatus.ERROR.value if failed_parameters else TestStatus.SUCCESS.value
- ),
+ "status": overall_status,
}
)
self.result["message"] += "HA Parameter Validation completed successfully. "
+
+ def _validate_basic_constants(self, category):
+ """
+ Validate basic configuration constants with offline validation support.
+ Uses existing CIB parsing logic but focuses on constants-first approach.
+ Creates dynamic subcategories based on element IDs found in CIB.
+
+ :param category: The category to validate (crm_config, rsc_defaults, op_defaults)
+ :type category: str
+ :return: A list of parameter dictionaries
+ :rtype: list
+ """
+ parameters = []
+
+ if category not in self.BASIC_CATEGORIES:
+ return parameters
+
+ _, constants_key = self.BASIC_CATEGORIES[category]
+ category_constants = self.constants.get(constants_key, {})
+
+ for param_name, expected_config in category_constants.items():
+ param_value, param_id = self._find_param_with_element_info(category, param_name)
+ expected_result = self._get_expected_value(category, param_name)
+ if expected_result:
+ expected_value, is_required = expected_result
+ expected_config_tuple = (expected_value, is_required)
+ else:
+ if isinstance(expected_config, dict):
+ expected_value = expected_config.get("value", "")
+ is_required = expected_config.get("required", False)
+ expected_config_tuple = (expected_value, is_required)
+ else:
+ expected_value = str(expected_config)
+ expected_config_tuple = (expected_value, False)
+
+ parameters.append(
+ self._create_parameter(
+ category=category,
+ name=param_name,
+ value=param_value,
+ expected_value=expected_config_tuple,
+ subcategory=param_id if param_id else "",
+ id=param_id,
+ )
+ )
+
+ return parameters
+
+ def _find_param_with_element_info(self, category, param_name):
+ """
+ Find a parameter value and its own unique ID in CIB XML.
+ Returns both the parameter value and the parameter's own ID (not container ID).
+
+ :param category: The category scope to search in (crm_config, rsc_defaults, op_defaults)
+ :type category: str
+ :param param_name: The parameter name to find
+ :type param_name: str
+ :return: Tuple of (parameter_value, parameter_id) or ("", "") if not found
+ :rtype: tuple(str, str)
+ """
+ param_value, param_id = "", ""
+ try:
+ if self.cib_output:
+ root = self._get_scope_from_cib(category)
+ else:
+ root = self.parse_xml_output(
+ self.execute_command_subprocess(CIB_ADMIN(scope=category))
+ )
+
+ if not root:
+ return param_value, param_id
+
+ if category in self.BASIC_CATEGORIES:
+ for element in root.findall(self.BASIC_CATEGORIES[category][0]):
+ for nvpair in element.findall(".//nvpair"):
+ if nvpair.get("name") == param_name:
+ param_id = nvpair.get("id", "")
+ param_value = nvpair.get("value", "")
+ return param_value, param_id
+
+ except Exception as ex:
+ self.result[
+ "message"
+ ] += f"Error finding parameter {param_name} in {category}: {str(ex)} "
+
+ return param_value, param_id
+
+ def _validate_resource_constants(self):
+ """
+ Resource validation - to be overridden by subclasses.
+ Base implementation returns empty list.
+
+ :return: A list of parameter dictionaries
+ :rtype: list
+ """
+ return []
+
+ def _validate_constraint_constants(self):
+ """
+ Validate constraint constants with offline validation support.
+ Uses constants-first approach to validate constraints against CIB.
+
+ :return: A list of parameter dictionaries
+ :rtype: list
+ """
+ parameters = []
+
+ if "CONSTRAINTS" not in self.constants:
+ return parameters
+
+ try:
+ if self.cib_output:
+ constraints_scope = self._get_scope_from_cib("constraints")
+ else:
+ constraints_scope = self.parse_xml_output(
+ self.execute_command_subprocess(CIB_ADMIN(scope="constraints"))
+ )
+
+ if constraints_scope is not None:
+ for constraint_type, constraint_config in self.constants["CONSTRAINTS"].items():
+ elements = constraints_scope.findall(f".//{constraint_type}")
+
+ for element in elements:
+ for attr_name, expected_config in constraint_config.items():
+ actual_value = element.get(attr_name, "")
+ expected_value = (
+ expected_config.get("value")
+ if isinstance(expected_config, dict)
+ else expected_config
+ )
+
+ parameters.append(
+ self._create_parameter(
+ category="constraints",
+ subcategory=constraint_type,
+ id=element.get("id", ""),
+ name=attr_name,
+ value=actual_value,
+ expected_value=expected_value,
+ )
+ )
+
+ except Exception as ex:
+ self.result["message"] += f"Error validating constraint constants: {str(ex)} "
+
+ return parameters
diff --git a/src/module_utils/sap_automation_qa.py b/src/module_utils/sap_automation_qa.py
index 4dc6dae2..bd94b369 100644
--- a/src/module_utils/sap_automation_qa.py
+++ b/src/module_utils/sap_automation_qa.py
@@ -7,7 +7,7 @@
import sys
import logging
import subprocess
-from typing import Optional, Dict, Any
+from typing import Dict, Any
import xml.etree.ElementTree as ET
try:
diff --git a/src/modules/get_pcmk_properties_db.py b/src/modules/get_pcmk_properties_db.py
index 2eeedb15..843da940 100644
--- a/src/modules/get_pcmk_properties_db.py
+++ b/src/modules/get_pcmk_properties_db.py
@@ -18,9 +18,11 @@
try:
from ansible.module_utils.get_pcmk_properties import BaseHAClusterValidator
from ansible.module_utils.enums import OperatingSystemFamily, HanaSRProvider
+ from ansible.module_utils.commands import CIB_ADMIN
except ImportError:
from src.module_utils.get_pcmk_properties import BaseHAClusterValidator
from src.module_utils.enums import OperatingSystemFamily, HanaSRProvider
+ from src.module_utils.commands import CIB_ADMIN
DOCUMENTATION = r"""
---
@@ -194,7 +196,7 @@ def __init__(
)
self.instance_number = instance_number
self.saphanasr_provider = saphanasr_provider
- self.parse_ha_cluster_config()
+ self.validate_from_constants()
def _parse_resources_section(self, root):
"""
@@ -211,6 +213,8 @@ def _parse_resources_section(self, root):
resource_categories.pop("topology", None)
else:
resource_categories.pop("angi_topology", None)
+ resource_categories.pop("angi_filesystem", None)
+ resource_categories.pop("angi_hana", None)
for sub_category, xpath in resource_categories.items():
elements = root.findall(xpath)
@@ -219,6 +223,31 @@ def _parse_resources_section(self, root):
return parameters
+ def _validate_resource_constants(self):
+ """
+ Resource validation with HANA-specific logic and offline validation support.
+ Validates resource constants by iterating through expected parameters.
+
+ :return: A list of parameter dictionaries
+ :rtype: list
+ """
+ parameters = []
+
+ try:
+ if self.cib_output:
+ resource_scope = self._get_scope_from_cib("resources")
+ else:
+ resource_scope = self.parse_xml_output(
+ self.execute_command_subprocess(CIB_ADMIN(scope="resources"))
+ )
+ if resource_scope is not None:
+ parameters.extend(self._parse_resources_section(resource_scope))
+
+ except Exception as ex:
+ self.result["message"] += f"Error validating resource constants: {str(ex)} "
+
+ return parameters
+
def _parse_global_ini_parameters(self):
"""
Parse global.ini parameters specific to SAP HANA.
@@ -255,22 +284,29 @@ def _parse_global_ini_parameters(self):
if sep
}
- for param_name, expected_value in global_ini_defaults.items():
+ for param_name, expected_config in global_ini_defaults.items():
value = global_ini_properties.get(param_name, "")
- if isinstance(expected_value, list):
- if value in expected_value:
- expected_value = value
+ if isinstance(expected_config, dict):
+ expected_value = expected_config.get("value")
+ is_required = expected_config.get("required", False)
+ else:
+ expected_value = expected_config
+ is_required = False
self.log(
logging.INFO,
- f"param_name: {param_name}, value: {value}, expected_value: {expected_value}",
+ f"param_name: {param_name}, value: {value}, expected_value: {expected_config}",
)
parameters.append(
self._create_parameter(
category="global_ini",
name=param_name,
value=value,
- expected_value=expected_value,
+ expected_value=(
+ expected_config.get("value")
+ if isinstance(expected_config, dict)
+ else expected_value
+ ),
)
)
except Exception as ex:
diff --git a/src/modules/get_pcmk_properties_scs.py b/src/modules/get_pcmk_properties_scs.py
index 8b3d95d5..59680f1c 100644
--- a/src/modules/get_pcmk_properties_scs.py
+++ b/src/modules/get_pcmk_properties_scs.py
@@ -17,9 +17,11 @@
try:
from ansible.module_utils.get_pcmk_properties import BaseHAClusterValidator
from ansible.module_utils.enums import OperatingSystemFamily, TestStatus
+ from ansible.module_utils.commands import CIB_ADMIN
except ImportError:
from src.module_utils.get_pcmk_properties import BaseHAClusterValidator
from src.module_utils.enums import OperatingSystemFamily, TestStatus
+ from src.module_utils.commands import CIB_ADMIN
DOCUMENTATION = r"""
@@ -191,7 +193,7 @@ def __init__(
self.scs_instance_number = scs_instance_number
self.ers_instance_number = ers_instance_number
self.nfs_provider = nfs_provider
- self.parse_ha_cluster_config()
+ self.validate_from_constants()
def _get_expected_value_for_category(self, category, subcategory, name, op_name):
"""
@@ -218,17 +220,49 @@ def _get_expected_value_for_category(self, category, subcategory, name, op_name)
else:
return self._get_expected_value(category, name)
+ def _validate_resource_constants(self):
+ """
+ Resource validation with SCS-specific logic and offline validation support.
+ Validates resource constants by iterating through expected parameters.
+
+ :return: A list of parameter dictionaries
+ :rtype: list
+ """
+ parameters = []
+
+ try:
+ if self.cib_output:
+ resource_scope = self._get_scope_from_cib("resources")
+ else:
+ resource_scope = self.parse_xml_output(
+ self.execute_command_subprocess(CIB_ADMIN(scope="resources"))
+ )
+
+ if resource_scope is not None:
+ parameters.extend(self._parse_resources_section(resource_scope))
+
+ except Exception as ex:
+ self.result["message"] += f"Error validating resource constants: {str(ex)} "
+
+ return parameters
+
def _determine_parameter_status(self, value, expected_value):
"""
Determine the status of a parameter with SCS-specific logic for NFS provider.
:param value: The actual value of the parameter.
:type value: str
- :param expected_value: The expected value of the parameter.
- :type expected_value: str or list or dict
+ :param expected_value: The expected value tuple (value, required) or legacy format.
+ :type expected_value: tuple or str or list or dict
:return: The status of the parameter.
:rtype: str
"""
+ 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
+
if expected_value is None or value == "":
return TestStatus.INFO.value
elif isinstance(expected_value, (str, list)):
@@ -245,12 +279,38 @@ def _determine_parameter_status(self, value, expected_value):
else TestStatus.ERROR.value
)
elif isinstance(expected_value, dict):
- provider_values = expected_value.get(self.nfs_provider, expected_value.get("AFS", []))
- return (
- TestStatus.SUCCESS.value
- if str(value) in provider_values
- else TestStatus.ERROR.value
- )
+ 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
+ )
else:
return TestStatus.ERROR.value
diff --git a/src/roles/ha_db_hana/tasks/files/constants.yaml b/src/roles/ha_db_hana/tasks/files/constants.yaml
index fa103cdf..f2865b5d 100644
--- a/src/roles/ha_db_hana/tasks/files/constants.yaml
+++ b/src/roles/ha_db_hana/tasks/files/constants.yaml
@@ -7,330 +7,706 @@
# === CRM Configuration Defaults ===
# cibadmin --query --scope crm_config
CRM_CONFIG_DEFAULTS:
- cluster-infrastructure: corosync
- priority-fencing-delay: ['30', '30s']
- stonith-action: reboot
- stonith-enabled: 'true'
- concurrent-fencing: 'true'
- maintenance-mode: 'false'
- node-health-strategy: 'custom'
- azure-events-az_globalPullState: 'IDLE'
+ cluster-infrastructure:
+ value: corosync
+ required: false
+ priority-fencing-delay:
+ value: ["30", "30s"]
+ required: true
+ stonith-action:
+ value: reboot
+ required: false
+ stonith-enabled:
+ value: "true"
+ required: false
+ concurrent-fencing:
+ value: "true"
+ required: false
+ maintenance-mode:
+ value: "false"
+ required: false
+ node-health-strategy:
+ value: "custom"
+ required: false
+ azure-events-az_globalPullState:
+ value: "IDLE"
+ required: false
# === Operation Defaults ===
# cibadmin --query --scope op_defaults
OP_DEFAULTS:
- record-pending: 'true'
- timeout: ['600', '600s']
+ record-pending:
+ value: "true"
+ required: false
+ timeout:
+ value: ["600", "600s"]
+ required: false
# === Resource Defaults ===
# cibadmin --query --scope rsc_defaults
RSC_DEFAULTS:
- migration-threshold: '5000'
- priority: '1'
- resource-stickiness: '1000'
+ migration-threshold:
+ value: "5000"
+ required: false
+ priority:
+ value: "1"
+ required: false
+ resource-stickiness:
+ value: "1000"
+ required: false
# === Constraints ===
# cibadmin --query --scope constraints
CONSTRAINTS:
rsc_colocation:
- score: "4000"
- rsc-role: "Started"
+ score:
+ value: "4000"
+ required: false
+ rsc-role:
+ value: "Started"
+ required: false
rsc_order:
- kind: "Optional"
+ kind:
+ value: "Optional"
+ required: false
# === Valid Configurations for different OS versions ===
# Specify the properties that are different for different OS versions
VALID_CONFIGS:
REDHAT:
- priority-fencing-delay: ['15', '15s']
+ priority-fencing-delay:
+ value: ["15", "15s"]
+ required: true
SUSE: {}
AFA:
- have-watchdog: "false"
- stonith-timeout: ["900s", "900"]
+ have-watchdog:
+ value: "false"
+ required: true
+ stonith-timeout:
+ value: ["900s", "900"]
+ required: true
ISCSI:
- have-watchdog: "true"
- stonith-timeout: ["210", "210s"]
-
+ have-watchdog:
+ value: "true"
+ required: true
+ stonith-timeout:
+ value: ["210", "210s"]
+ required: true
+ ASD:
+ have-watchdog:
+ value: "true"
+ required: true
+ stonith-timeout:
+ value: ["210", "210s"]
+ required: true
# === Resource Defaults ===
# cibadmin --query --scope resources
RESOURCE_DEFAULTS:
SUSE:
fence_agent:
+ required: false
instance_attributes:
- pcmk_delay_max: "15"
- pcmk_monitor_retries: "4"
- pcmk_action_limit: "3"
- pcmk_reboot_timeout: ["900", "900s"]
- power_timeout: ["240", "240s"]
- pcmk_monitor_timeout: ["120", "120s"]
+ pcmk_delay_max:
+ value: "15"
+ required: false
+ pcmk_monitor_retries:
+ value: "4"
+ required: false
+ pcmk_action_limit:
+ value: "3"
+ required: false
+ pcmk_reboot_timeout:
+ value: ["900", "900s"]
+ required: false
+ power_timeout:
+ value: ["240", "240s"]
+ required: false
+ pcmk_monitor_timeout:
+ value: ["120", "120s"]
+ required: false
operations:
monitor:
- interval: ["3600", "3600s"]
- timeout: ["120", "120s"]
+ interval:
+ value: ["3600", "3600s"]
+ required: false
+ timeout:
+ value: ["120", "120s"]
+ required: false
sbd_stonith:
+ required: false
instance_attributes:
- pcmk_delay_max: "15"
- pcmk_monitor_retries: "4"
- pcmk_action_limit: "3"
- pcmk_reboot_timeout: ["900", "900s"]
- power_timeout: ["240", "240s"]
- pcmk_monitor_timeout: ["120", "120s"]
+ pcmk_delay_max:
+ value: "15"
+ required: false
+ pcmk_monitor_retries:
+ value: "4"
+ required: false
+ pcmk_action_limit:
+ value: "3"
+ required: false
+ pcmk_reboot_timeout:
+ value: ["900", "900s"]
+ required: false
+ power_timeout:
+ value: ["240", "240s"]
+ required: false
+ pcmk_monitor_timeout:
+ value: ["120", "120s"]
+ required: false
operations:
monitor:
- interval: ["600", "600s"]
- timeout: ["15", "15s"]
+ interval:
+ value: ["600", "600s"]
+ required: false
+ timeout:
+ value: ["15", "15s"]
+ required: false
topology:
+ required: false
meta_attributes:
- clone-node-max: "1"
- target-role: "Started"
- interleave: "true"
+ clone-node-max:
+ value: "1"
+ required: false
+ target-role:
+ value: "Started"
+ required: false
+ interleave:
+ value: "true"
+ required: false
operations:
monitor:
- interval: ["10", "10s"]
- timeout: ["600", "600s"]
+ interval:
+ value: ["10", "10s"]
+ required: false
+ timeout:
+ value: ["600", "600s"]
+ required: false
start:
- interval: ["0", "0s"]
- timeout: ["600", "600s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["600", "600s"]
+ required: false
stop:
- interval: ["0", "0s"]
- timeout: ["300", "300s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["300", "300s"]
+ required: false
angi_topology:
+ required: false
meta_attributes:
- clone-node-max: "1"
- target-role: "Started"
- interleave: "true"
+ clone-node-max:
+ value: "1"
+ required: false
+ target-role:
+ value: "Started"
+ required: false
+ interleave:
+ value: "true"
+ required: false
operations:
monitor:
- interval: ["50", "50s"]
- timeout: ["600", "600s"]
+ interval:
+ value: ["50", "50s"]
+ required: false
+ timeout:
+ value: ["600", "600s"]
+ required: false
start:
- interval: ["0", "0s"]
- timeout: ["600", "600s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["600", "600s"]
+ required: false
stop:
- interval: ["0", "0s"]
- timeout: ["300", "300s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["300", "300s"]
+ required: false
hana:
+ required: false
meta_attributes:
- notify: "true"
- clone-max: "2"
- clone-node-max: "1"
- target-role: "Started"
- interleave: "true"
- priority: "100"
+ notify:
+ value: "true"
+ required: false
+ clone-max:
+ value: "2"
+ required: false
+ clone-node-max:
+ value: "1"
+ required: false
+ target-role:
+ value: "Started"
+ required: false
+ interleave:
+ value: "true"
+ required: false
+ priority:
+ value: "100"
+ required: false
instance_attributes:
- PREFER_SITE_TAKEOVER: "true"
- DUPLICATE_PRIMARY_TIMEOUT: "7200"
- AUTOMATED_REGISTER: "true"
+ PREFER_SITE_TAKEOVER:
+ value: "true"
+ required: false
+ DUPLICATE_PRIMARY_TIMEOUT:
+ value: "7200"
+ required: false
+ AUTOMATED_REGISTER:
+ value: "true"
+ required: false
operations:
start:
- interval: ["0", "0s"]
- timeout: ["3600", "3600s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["3600", "3600s"]
+ required: false
stop:
- interval: ["0", "0s"]
- timeout: ["3600", "3600s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["3600", "3600s"]
+ required: false
promote:
- interval: ["0", "0s"]
- timeout: ["3600", "3600s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["3600", "3600s"]
+ required: false
monitor:
- timeout: ["700", "700s"]
+ timeout:
+ value: ["700", "700s"]
+ required: false
angi_hana:
+ required: false
meta_attributes:
- notify: "true"
- clone-max: "2"
- clone-node-max: "1"
- target-role: "Started"
- interleave: "true"
- priority: "100"
+ notify:
+ value: "true"
+ required: false
+ clone-max:
+ value: "2"
+ required: false
+ clone-node-max:
+ value: "1"
+ required: false
+ target-role:
+ value: "Started"
+ required: false
+ interleave:
+ value: "true"
+ required: false
+ priority:
+ value: "100"
+ required: false
instance_attributes:
- PREFER_SITE_TAKEOVER: "true"
- DUPLICATE_PRIMARY_TIMEOUT: "7200"
- AUTOMATED_REGISTER: "true"
+ PREFER_SITE_TAKEOVER:
+ value: "true"
+ required: false
+ DUPLICATE_PRIMARY_TIMEOUT:
+ value: "7200"
+ required: false
+ AUTOMATED_REGISTER:
+ value: "true"
+ required: false
operations:
start:
- interval: ["0", "0s"]
- timeout: ["3600", "3600s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["3600", "3600s"]
+ required: false
stop:
- interval: ["0", "0s"]
- timeout: ["3600", "3600s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["3600", "3600s"]
+ required: false
promote:
- interval: ["0", "0s"]
- timeout: ["3600", "3600s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["3600", "3600s"]
+ required: false
demote:
- interval: ["0", "0s"]
- timeout: ["320", "320s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["320", "320s"]
+ required: false
monitor:
- timeout: ["700", "700s"]
+ timeout:
+ value: ["700", "700s"]
+ required: false
ipaddr:
+ required: false
meta_attributes:
- target-role: "Started"
+ target-role:
+ value: "Started"
+ required: false
operations:
monitor:
- interval: ["10", "10s"]
- timeout: ["20", "20s"]
+ interval:
+ value: ["10", "10s"]
+ required: false
+ timeout:
+ value: ["20", "20s"]
+ required: false
filesystem:
+ required: false
meta_attributes:
- clone-node-max: "1"
- interleave: "true"
+ clone-node-max:
+ value: "1"
+ required: false
+ interleave:
+ value: "true"
+ required: false
operations:
monitor:
- interval: ["120", "120s"]
- timeout: ["120", "120s"]
+ interval:
+ value: ["120", "120s"]
+ required: false
+ timeout:
+ value: ["120", "120s"]
+ required: false
start:
- interval: ["0", "0s"]
- timeout: ["120", "120s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["120", "120s"]
+ required: false
stop:
- interval: ["0", "0s"]
- timeout: ["120", "120s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["120", "120s"]
+ required: false
angi_filesystem:
+ required: false
meta_attributes:
- clone-node-max: "1"
- interleave: "true"
+ clone-node-max:
+ value: "1"
+ required: true
+ interleave:
+ value: "true"
+ required: false
operations:
monitor:
- interval: ["120", "120s"]
- timeout: ["120", "120s"]
+ interval:
+ value: ["120", "120s"]
+ required: false
+ timeout:
+ value: ["120", "120s"]
+ required: false
start:
- interval: ["0", "0s"]
- timeout: ["10", "10s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["10", "10s"]
+ required: false
stop:
- interval: ["0", "0s"]
- timeout: ["20", "20s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["20", "20s"]
+ required: false
azurelb:
+ required: false
meta_attributes:
- resource-stickiness: "0"
+ resource-stickiness:
+ value: "0"
+ required: false
operations:
monitor:
- interval: ["10", "10s"]
- timeout: ["20", "20s"]
-
+ interval:
+ value: ["10", "10s"]
+ required: false
+ timeout:
+ value: ["20", "20s"]
+ required: false
REDHAT:
fence_agent:
+ required: false
instance_attributes:
- pcmk_delay_max: "15"
- pcmk_monitor_retries: "4"
- pcmk_action_limit: "3"
- pcmk_reboot_timeout: "900"
- power_timeout: "240"
- pcmk_monitor_timeout: "120"
+ pcmk_delay_max:
+ value: "15"
+ required: false
+ pcmk_monitor_retries:
+ value: "4"
+ required: false
+ pcmk_action_limit:
+ value: "3"
+ required: false
+ pcmk_reboot_timeout:
+ value: "900"
+ required: false
+ power_timeout:
+ value: "240"
+ required: false
+ pcmk_monitor_timeout:
+ value: "120"
+ required: false
operations:
monitor:
- interval: ["3600", "3600s"]
+ interval:
+ value: ["3600", "3600s"]
+ required: false
sbd_stonith:
+ required: false
instance_attributes:
- pcmk_monitor_retries: "4"
- pcmk_action_limit: "3"
- pcmk_reboot_timeout: ["900", "900s"]
- power_timeout: ["240", "240s"]
- pcmk_monitor_timeout: ["120", "120s"]
+ pcmk_monitor_retries:
+ value: "4"
+ required: false
+ pcmk_action_limit:
+ value: "3"
+ required: false
+ pcmk_reboot_timeout:
+ value: ["900", "900s"]
+ required: false
+ power_timeout:
+ value: ["240", "240s"]
+ required: false
+ pcmk_monitor_timeout:
+ value: ["120", "120s"]
+ required: false
operations:
monitor:
- interval: ["600", "600s"]
- timeout: ["15", "15s"]
+ interval:
+ value: ["600", "600s"]
+ required: false
+ timeout:
+ value: ["15", "15s"]
+ required: false
topology:
+ required: false
meta_attributes:
- clone-node-max: "1"
- clone-max: "2"
- target-role: "Started"
- interleave: "true"
- failure-timeout: "120s"
+ clone-node-max:
+ value: "1"
+ required: false
+ clone-max:
+ value: "2"
+ required: false
+ target-role:
+ value: "Started"
+ required: false
+ interleave:
+ value: "true"
+ required: false
+ failure-timeout:
+ value: "120s"
+ required: false
operations:
monitor:
- interval: ["10", "10s"]
- timeout: ["600", "600s"]
+ interval:
+ value: ["10", "10s"]
+ required: false
+ timeout:
+ value: ["600", "600s"]
+ required: false
start:
- interval: ["0", "0s"]
- timeout: ["600", "600s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["600", "600s"]
+ required: false
stop:
- interval: ["0", "0s"]
- timeout: ["300", "300s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["300", "300s"]
+ required: false
methods:
- timeout: ["5", "5s"]
- interval: ["0", "0s"]
+ timeout:
+ value: ["5", "5s"]
+ required: false
+ interval:
+ value: ["0", "0s"]
+ required: false
reload:
- timeout: ["5", "5s"]
- interval: ["0", "0s"]
+ timeout:
+ value: ["5", "5s"]
+ required: false
+ interval:
+ value: ["0", "0s"]
+ required: false
hana:
+ required: false
meta_attributes:
- notify: "true"
- clone-max: "2"
- clone-node-max: "1"
- target-role: "Started"
- interleave: "true"
- priority: "100"
+ notify:
+ value: "true"
+ required: false
+ clone-max:
+ value: "2"
+ required: false
+ clone-node-max:
+ value: "1"
+ required: false
+ target-role:
+ value: "Started"
+ required: false
+ interleave:
+ value: "true"
+ required: false
+ priority:
+ value: "100"
+ required: true
instance_attributes:
- PREFER_SITE_TAKEOVER: "true"
- DUPLICATE_PRIMARY_TIMEOUT: "7200"
- AUTOMATED_REGISTER: "true"
+ PREFER_SITE_TAKEOVER:
+ value: "true"
+ required: false
+ DUPLICATE_PRIMARY_TIMEOUT:
+ value: "7200"
+ required: false
+ AUTOMATED_REGISTER:
+ value: "true"
+ required: false
operations:
start:
- interval: ["0", "0s"]
- timeout: ["3600", "3600s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["3600", "3600s"]
+ required: false
stop:
- interval: ["0", "0s"]
- timeout: ["3600", "3600s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["3600", "3600s"]
+ required: false
promote:
- interval: ["0", "0s"]
- timeout: ["3600", "3600s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["3600", "3600s"]
+ required: false
monitor:
- timeout: ["700", "700s"]
+ timeout:
+ value: ["700", "700s"]
+ required: false
ipaddr:
meta_attributes:
- target-role: "Started"
+ target-role:
+ value: "Started"
+ required: false
operations:
monitor:
- interval: ["10", "10s"]
- timeout: ["20", "20s"]
+ interval:
+ value: ["10", "10s"]
+ required: false
+ timeout:
+ value: ["20", "20s"]
+ required: false
start:
- interval: ["0", "0s"]
- timeout: ["20", "20s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["20", "20s"]
+ required: false
stop:
- interval: ["0", "0s"]
- timeout: ["20", "20s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["20", "20s"]
+ required: false
filesystem:
+ required: false
meta_attributes:
- clone-node-max: "1"
- interleave: "true"
+ clone-node-max:
+ value: "1"
+ required: false
+ interleave:
+ value: "true"
+ required: false
operations:
monitor:
- interval: ["20", "20s"]
- timeout: ["120", "120s"]
+ interval:
+ value: ["20", "20s"]
+ required: false
+ timeout:
+ value: ["120", "120s"]
+ required: false
start:
- interval: ["0", "0s"]
- timeout: ["60", "60s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["60", "60s"]
+ required: false
stop:
- interval: ["0", "0s"]
- timeout: ["60", "60s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["60", "60s"]
+ required: false
azurelb:
+ required: false
meta_attributes:
- resource-stickiness: "0"
+ resource-stickiness:
+ value: "0"
+ required: false
operations:
monitor:
- interval: ["10", "10s"]
- timeout: ["20", "20s"]
+ interval:
+ value: ["10", "10s"]
+ required: false
+ timeout:
+ value: ["20", "20s"]
+ required: false
start:
- interval: ["0", "0s"]
- timeout: ["20", "20s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["20", "20s"]
+ required: false
stop:
- interval: ["0", "0s"]
- timeout: ["20", "20s"]
-
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["20", "20s"]
+ required: false
# === OS Parameters ===
# Run command as root. Format of command is: "parent_key child_key"
@@ -338,39 +714,74 @@ RESOURCE_DEFAULTS:
OS_PARAMETERS:
DEFAULTS:
sysctl:
- net.ipv4.tcp_timestamps: "net.ipv4.tcp_timestamps = 0"
- vm.swappiness: "vm.swappiness = 10"
+ net.ipv4.tcp_timestamps:
+ value: "net.ipv4.tcp_timestamps = 0"
+ required: true
+ vm.swappiness:
+ value: "vm.swappiness = 10"
+ required: true
corosync-cmapctl:
- runtime.config.totem.token: "runtime.config.totem.token (u32) = 30000"
- runtime.config.totem.consensus: "runtime.config.totem.consensus (u32) = 36000"
+ runtime.config.totem.token:
+ value: "runtime.config.totem.token (u32) = 30000"
+ required: true
+ runtime.config.totem.consensus:
+ value: "runtime.config.totem.consensus (u32) = 36000"
+ required: true
# === 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: "SAPHanaSR"
- path: ["/usr/share/SAPHanaSR", "/hana/shared/myHooks"]
- execution_order: "1"
+ provider:
+ value: "SAPHanaSR"
+ required: true
+ path:
+ value: ["/usr/share/SAPHanaSR", "/hana/shared/myHooks"]
+ required: true
+ execution_order:
+ value: "1"
+ required: true
SAPHanaSR-angi:
- provider: "susHanaSR"
- path: ["/usr/share/SAPHanaSR", "/hana/shared/myHooks"]
- execution_order: "1"
+ provider:
+ value: "susHanaSR"
+ required: true
+ path:
+ value: ["/usr/share/SAPHanaSR", "/hana/shared/myHooks"]
+ required: true
+ execution_order:
+ value: "1"
+ required: true
REDHAT:
SAPHanaSR:
- provider: "SAPHanaSR"
- path: ["/usr/share/SAPHanaSR/srHook", "/hana/shared/myHooks"]
- execution_order: "1"
-
+ provider:
+ value: "SAPHanaSR"
+ required: true
+ path:
+ value: ["/usr/share/SAPHanaSR/srHook", "/hana/shared/myHooks"]
+ required: true
+ execution_order:
+ value: "1"
+ required: true
# === Azure Load Balancer ===
# Azure Load Balancer configuration
AZURE_LOADBALANCER:
PROBES:
- probe_threshold: 2
- interval_in_seconds: 5
-
+ probe_threshold:
+ value: 2
+ required: true
+ interval_in_seconds:
+ value: 5
+ required: true
RULES:
- idle_timeout_in_minutes: 30
- enable_floating_ip: true
- enable_tcp_reset: false
+ idle_timeout_in_minutes:
+ value: 30
+ required: true
+ enable_floating_ip:
+ value: true
+ required: true
+ enable_tcp_reset:
+ value: false
+ required: false
diff --git a/src/roles/ha_scs/tasks/files/constants.yaml b/src/roles/ha_scs/tasks/files/constants.yaml
index 8fec70fc..3348fccf 100644
--- a/src/roles/ha_scs/tasks/files/constants.yaml
+++ b/src/roles/ha_scs/tasks/files/constants.yaml
@@ -7,324 +7,675 @@
# === CRM Configuration Defaults ===
# cibadmin --query --scope crm_config
CRM_CONFIG_DEFAULTS:
- cluster-infrastructure: corosync
- priority-fencing-delay: ["30", "30s"]
- stonith-action: reboot
- stonith-enabled: "true"
- concurrent-fencing: "true"
- maintenance-mode: "false"
- node-health-strategy: "custom"
- azure-events-az_globalPullState: "IDLE"
+ cluster-infrastructure:
+ value: corosync
+ required: false
+ priority-fencing-delay:
+ value: ["30", "30s"]
+ required: true
+ stonith-action:
+ value: reboot
+ required: false
+ stonith-enabled:
+ value: "true"
+ required: false
+ concurrent-fencing:
+ value: "true"
+ required: false
+ maintenance-mode:
+ value: "false"
+ required: false
+ node-health-strategy:
+ value: "custom"
+ required: false
+ azure-events-az_globalPullState:
+ value: "IDLE"
+ required: false
# === Operation Defaults ===
# cibadmin --query --scope op_defaults
OP_DEFAULTS:
- record-pending: "true"
- timeout: ["600", "600s"]
+ record-pending:
+ value: "true"
+ required: false
+ timeout:
+ value: ["600", "600s"]
+ required: false
# === Resource Defaults ===
# cibadmin --query --scope rsc_defaults
RSC_DEFAULTS:
- migration-threshold: "3"
- priority: "1"
- resource-stickiness: "1"
+ migration-threshold:
+ value: "3"
+ required: false
+ priority:
+ value: "1"
+ required: false
+ resource-stickiness:
+ value: "1"
+ required: false
# === Constraints ===
# cibadmin --query --scope constraints
CONSTRAINTS:
rsc_colocation:
- score: "-5000"
- rsc-role: "Started"
- with-rsc-role: "Started"
+ score:
+ value: "-5000"
+ required: false
+ rsc-role:
+ value: "Started"
+ required: false
+ with-rsc-role:
+ value: "Started"
+ required: false
rsc_order:
- first-action: "start"
- then-action: "stop"
- symmetrical: "false"
+ first-action:
+ value: "start"
+ required: false
+ then-action:
+ value: "stop"
+ required: false
+ symmetrical:
+ value: "false"
+ required: false
rsc_location:
- score-attribute: "#health-azure"
- operation: "defined"
- attribute: "#uname"
+ score-attribute:
+ value: "#health-azure"
+ required: false
+ operation:
+ value: "defined"
+ required: false
+ attribute:
+ value: "#uname"
+ required: false
# === Valid Configurations for different OS versions ===
# Specify the properties that are different for different OS versions
VALID_CONFIGS:
REDHAT:
- priority-fencing-delay: "15s"
+ priority-fencing-delay:
+ value: ["15", "15s"]
+ required: false
SUSE: {}
AFA:
- have-watchdog: "false"
- stonith-timeout: ["900", "900s"]
+ have-watchdog:
+ value: "false"
+ required: false
+ stonith-timeout:
+ value: ["900", "900s"]
+ required: false
ISCSI:
- have-watchdog: "true"
- stonith-timeout: ["210", "210s"]
+ have-watchdog:
+ value: "true"
+ required: false
+ stonith-timeout:
+ value: ["210", "210s"]
+ required: false
+ ASD:
+ have-watchdog:
+ value: "true"
+ required: false
+ stonith-timeout:
+ value: ["210", "210s"]
+ required: false
# === Resource Defaults ===
# cibadmin --query --scope resources
RESOURCE_DEFAULTS:
SUSE:
fence_agent:
+ required: false
instance_attributes:
- pcmk_delay_max: "15"
- pcmk_monitor_retries: "4"
- pcmk_action_limit: "3"
- pcmk_reboot_timeout: ["900", "900s"]
- power_timeout: ["240", "240s"]
+ pcmk_delay_max:
+ value: "15"
+ required: false
+ pcmk_monitor_retries:
+ value: "4"
+ required: false
+ pcmk_action_limit:
+ value: "3"
+ required: false
+ pcmk_reboot_timeout:
+ value: ["900", "900s"]
+ required: false
+ power_timeout:
+ value: ["240", "240s"]
+ required: false
operations:
monitor:
- interval: ["3600", "3600s"]
- timeout: ["120", "120s"]
+ interval:
+ value: ["3600", "3600s"]
+ required: false
+ timeout:
+ value: ["120", "120s"]
+ required: false
start:
- interval: ["0", "0s"]
- timeout: ["20", "20s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["20", "20s"]
+ required: false
stop:
- interval: ["0", "0s"]
- timeout: ["20", "20s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["20", "20s"]
+ required: false
sbd_stonith:
instance_attributes:
- pcmk_delay_max: "15"
- pcmk_monitor_retries: "4"
- pcmk_action_limit: "3"
- pcmk_reboot_timeout: ["900", "900s"]
- power_timeout: ["240", "240s"]
- pcmk_monitor_timeout: ["120", "120s"]
+ pcmk_delay_max:
+ value: "15"
+ required: false
+ pcmk_monitor_retries:
+ value: "4"
+ required: false
+ pcmk_action_limit:
+ value: "3"
+ required: false
+ pcmk_reboot_timeout:
+ value: ["900", "900s"]
+ required: false
+ power_timeout:
+ value: ["240", "240s"]
+ required: false
+ pcmk_monitor_timeout:
+ value: ["120", "120s"]
+ required: false
operations:
monitor:
- interval: ["600", "600s"]
- timeout: ["15", "15s"]
+ interval:
+ value: ["600", "600s"]
+ required: false
+ timeout:
+ value: ["15", "15s"]
+ required: false
ascs:
instance_attributes:
- AUTOMATIC_RECOVER: "false"
- MINIMAL_PROBE: "true"
+ AUTOMATIC_RECOVER:
+ value: "false"
+ required: false
+ MINIMAL_PROBE:
+ value: "true"
+ required: false
meta_attributes:
- resource-stickiness: "5000"
- priority: "100"
+ resource-stickiness:
+ value: "5000"
+ required: false
+ priority:
+ value: "100"
+ required: true
operations:
monitor:
- interval: ["11", "11s"]
+ interval:
+ value: ["11", "11s"]
+ required: false
timeout:
- ANF: ["105", "105s"]
- AFS: ["60", "60s"]
+ ANF:
+ value: ["105", "105s"]
+ required: false
+ AFS:
+ value: ["60", "60s"]
+ required: false
start:
- interval: ["0", "0s"]
- timeout: ["180", "180s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["180", "180s"]
+ required: false
stop:
- interval: ["0", "0s"]
- timeout: ["240", "240s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["240", "240s"]
+ required: false
promote:
- interval: ["0", "0s"]
- timeout: ["320", "320s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["320", "320s"]
+ required: false
demote:
- interval: ["0", "0s"]
- timeout: ["320", "320s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["320", "320s"]
+ required: false
ers:
instance_attributes:
- AUTOMATIC_RECOVER: "false"
- MINIMAL_PROBE: "true"
- IS_ERS: "true"
+ AUTOMATIC_RECOVER:
+ value: "false"
+ required: false
+ MINIMAL_PROBE:
+ value: "true"
+ required: false
+ IS_ERS:
+ value: "true"
+ required: false
meta_attributes:
- resource-stickiness: "5000"
- priority: "100"
+ resource-stickiness:
+ value: "5000"
+ required: false
+ priority:
+ value: "100"
+ required: false
operations:
monitor:
- interval: ["11", "11s"]
+ interval:
+ value: ["11", "11s"]
+ required: false
timeout:
- ANF: ["105", "105s"]
- AFS: ["60", "60s"]
+ ANF:
+ value: ["105", "105s"]
+ required: false
+ AFS:
+ value: ["60", "60s"]
+ required: false
start:
- interval: ["0", "0s"]
- timeout: ["180", "180s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["180", "180s"]
+ required: false
stop:
- interval: ["0", "0s"]
- timeout: ["240", "240s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["240", "240s"]
+ required: false
promote:
- interval: ["0", "0s"]
- timeout: ["320", "320s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["320", "320s"]
+ required: false
demote:
- interval: ["0", "0s"]
- timeout: ["320", "320s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["320", "320s"]
+ required: false
ipaddr:
meta_attributes:
- target-role: "Started"
+ target-role:
+ value: "Started"
+ required: false
operations:
monitor:
- interval: ["10", "10s"]
- timeout: ["20", "20s"]
+ interval:
+ value: ["10", "10s"]
+ required: false
+ timeout:
+ value: ["20", "20s"]
+ required: false
start:
- interval: ["0", "0s"]
- timeout: ["20", "20s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["20", "20s"]
+ required: false
stop:
- interval: ["0", "0s"]
- timeout: ["20", "20s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["20", "20s"]
+ required: false
azurelb:
meta_attributes:
- resource-stickiness: "0"
+ resource-stickiness:
+ value: "0"
+ required: false
operations:
monitor:
- interval: ["10", "10s"]
- timeout: ["20", "20s"]
+ interval:
+ value: ["10", "10s"]
+ required: false
+ timeout:
+ value: ["20", "20s"]
+ required: false
start:
- interval: ["0", "0s"]
- timeout: ["20", "20s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["20", "20s"]
+ required: false
stop:
- interval: ["0", "0s"]
- timeout: ["20", "20s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["20", "20s"]
+ required: false
azureevents:
meta_attributes:
- allow-unhealthy-nodes: "true"
- failure-timeout: "120s"
+ allow-unhealthy-nodes:
+ value: "true"
+ required: false
+ failure-timeout:
+ value: "120s"
+ required: false
operations:
monitor:
- interval: ["10", "10s"]
+ interval:
+ value: ["10", "10s"]
+ required: false
start:
- interval: ["0", "0s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
REDHAT:
fence_agent:
instance_attributes:
- pcmk_delay_max: "15"
- pcmk_monitor_retries: "4"
- pcmk_action_limit: "3"
- pcmk_reboot_timeout: ["900", "900s"]
- power_timeout: ["240", "240s"]
+ pcmk_delay_max:
+ value: "15"
+ required: false
+ pcmk_monitor_retries:
+ value: "4"
+ required: false
+ pcmk_action_limit:
+ value: "3"
+ required: false
+ pcmk_reboot_timeout:
+ value: ["900", "900s"]
+ required: false
+ power_timeout:
+ value: ["240", "240s"]
+ required: false
operations:
monitor:
- interval: "3600"
- timeout: ["120", "120s"]
+ interval:
+ value: "3600"
+ required: false
+ timeout:
+ value: ["120", "120s"]
+ required: false
start:
- interval: ["0", "0s"]
- timeout: ["20", "20s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["20", "20s"]
+ required: false
stop:
- interval: ["0", "0s"]
- timeout: ["20", "20s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["20", "20s"]
+ required: false
sbd_stonith:
instance_attributes:
- pcmk_delay_max: "15"
- pcmk_monitor_retries: "4"
- pcmk_action_limit: "3"
- pcmk_reboot_timeout: ["900", "900s"]
- power_timeout: ["240", "240s"]
- pcmk_monitor_timeout: ["120", "120s"]
+ pcmk_delay_max:
+ value: "15"
+ required: false
+ pcmk_monitor_retries:
+ value: "4"
+ required: false
+ pcmk_action_limit:
+ value: "3"
+ required: false
+ pcmk_reboot_timeout:
+ value: ["900", "900s"]
+ required: false
+ power_timeout:
+ value: ["240", "240s"]
+ required: false
+ pcmk_monitor_timeout:
+ value: ["120", "120s"]
+ required: false
operations:
monitor:
- interval: "600"
- timeout: ["15", "15s"]
+ interval:
+ value: "600"
+ required: false
+ timeout:
+ value: ["15", "15s"]
+ required: false
ascs:
instance_attributes:
- AUTOMATIC_RECOVER: "false"
- MINIMAL_PROBE: "true"
- meta_attributes:
- resource-stickiness: "5000"
- priority: "10"
+ AUTOMATIC_RECOVER:
+ value: "false"
+ required: false
+ MINIMAL_PROBE:
+ value: "true"
+ required: false
operations:
- monitor:
- interval: ["20", "20s"]
- timeout:
- ANF: ["105", "105s"]
- AFS: ["60", "60s"]
start:
- interval: ["0", "0s"]
- timeout: ["600", "600s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["600", "600s"]
+ required: false
stop:
- interval: ["0", "0s"]
- timeout: ["600", "600s"]
- promote:
- interval: ["0", "0s"]
- timeout: ["320", "320s"]
- demote:
- interval: ["0", "0s"]
- timeout: ["320", "320s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["600", "600s"]
+ required: false
+ monitor:
+ interval:
+ value: ["20", "20s"]
+ required: false
+ timeout:
+ ANF:
+ value: ["105", "105s"]
+ required: false
+ AFS:
+ value: ["60", "60s"]
+ required: false
methods:
- timeout: ["5", "5s"]
- interval: ["0", "0s"]
+ timeout:
+ value: ["5", "5s"]
+ required: false
+ interval:
+ value: ["0", "0s"]
+ required: false
reload:
- timeout: ["320", "320s"]
- interval: ["0", "0s"]
+ timeout:
+ value: ["320", "320s"]
+ required: false
+ interval:
+ value: ["0", "0s"]
+ required: false
+ promote:
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["320", "320s"]
+ required: false
+ demote:
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["320", "320s"]
+ required: false
ers:
instance_attributes:
- AUTOMATIC_RECOVER: "false"
- MINIMAL_PROBE: "true"
- IS_ERS: "true"
+ AUTOMATIC_RECOVER:
+ value: "false"
+ required: false
+ MINIMAL_PROBE:
+ value: "true"
+ required: false
+ IS_ERS:
+ value: "true"
+ required: false
meta_attributes:
- resource-stickiness: "3000"
- priority: "100"
+ resource-stickiness:
+ value: "3000"
+ required: false
+ priority:
+ value: "100"
+ required: false
operations:
monitor:
- interval: ["20", "20s"]
+ interval:
+ value: ["20", "20s"]
+ required: false
timeout:
- ANF: ["105", "105s"]
- AFS: ["60", "60s"]
+ ANF:
+ value: ["105", "105s"]
+ required: false
+ AFS:
+ value: ["60", "60s"]
+ required: false
start:
- interval: ["0", "0s"]
- timeout: ["600", "600s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["600", "600s"]
+ required: false
stop:
- interval: ["0", "0s"]
- timeout: ["600", "600s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["600", "600s"]
+ required: false
promote:
- interval: ["0", "0s"]
- timeout: ["320", "320s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["320", "320s"]
+ required: false
demote:
- interval: ["0", "0s"]
- timeout: ["320", "320s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["320", "320s"]
+ required: false
methods:
- timeout: ["5", "5s"]
- interval: ["0", "0s"]
+ timeout:
+ value: ["5", "5s"]
+ required: false
+ interval:
+ value: ["0", "0s"]
+ required: false
reload:
- timeout: ["320", "320s"]
- interval: ["0", "0s"]
+ timeout:
+ value: ["320", "320s"]
+ required: false
+ interval:
+ value: ["0", "0s"]
+ required: false
ipaddr:
meta_attributes:
- target-role: "Started"
+ target-role:
+ value: "Started"
+ required: false
operations:
monitor:
- interval: ["10", "10s"]
- timeout: ["20", "20s"]
+ interval:
+ value: ["10", "10s"]
+ required: false
+ timeout:
+ value: ["20", "20s"]
+ required: false
start:
- interval: ["0", "0s"]
- timeout: ["20", "20s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["20", "20s"]
+ required: false
stop:
- interval: ["0", "0s"]
- timeout: ["20", "20s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["20", "20s"]
+ required: false
azurelb:
meta_attributes:
- resource-stickiness: "0"
+ resource-stickiness:
+ value: "0"
+ required: false
operations:
monitor:
- interval: ["10", "10s"]
- timeout: ["20", "20s"]
+ interval:
+ value: ["10", "10s"]
+ required: false
+ timeout:
+ value: ["20", "20s"]
+ required: false
start:
- interval: ["0", "0s"]
- timeout: ["20", "20s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["20", "20s"]
+ required: false
stop:
- interval: ["0", "0s"]
- timeout: ["20", "20s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["20", "20s"]
+ required: false
azureevents:
meta_attributes:
- allow-unhealthy-nodes: "true"
- failure-timeout: ["120", "120s"]
+ allow-unhealthy-nodes:
+ value: "true"
+ required: false
+ failure-timeout:
+ value: ["120", "120s"]
+ required: false
operations:
monitor:
- interval: ["10", "10s"]
- timeout: ["240", "240s"]
+ interval:
+ value: ["10", "10s"]
+ required: false
+ timeout:
+ value: ["240", "240s"]
+ required: false
start:
- interval: ["0", "0s"]
- timeout: ["10", "10s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["10", "10s"]
+ required: false
stop:
- interval: ["0", "0s"]
- timeout: ["10", "10s"]
+ interval:
+ value: ["0", "0s"]
+ required: false
+ timeout:
+ value: ["10", "10s"]
+ required: false
# === OS Parameters ===
@@ -333,20 +684,38 @@ RESOURCE_DEFAULTS:
OS_PARAMETERS:
DEFAULTS:
sysctl:
- net.ipv4.tcp_timestamps: "net.ipv4.tcp_timestamps = 0"
- vm.swappiness: "vm.swappiness = 10"
+ net.ipv4.tcp_timestamps:
+ value: "net.ipv4.tcp_timestamps = 0"
+ required: false
+ vm.swappiness:
+ value: "vm.swappiness = 10"
+ required: false
corosync-cmapctl:
- runtime.config.totem.token: "runtime.config.totem.token (u32) = 30000"
- runtime.config.totem.consensus: "runtime.config.totem.consensus (u32) = 36000"
+ runtime.config.totem.token:
+ value: "runtime.config.totem.token (u32) = 30000"
+ required: false
+ runtime.config.totem.consensus:
+ value: "runtime.config.totem.consensus (u32) = 36000"
+ required: false
# === Azure Load Balancer ===
# Azure Load Balancer configuration
AZURE_LOADBALANCER:
PROBES:
- probe_threshold: 2
- interval_in_seconds: 5
+ probe_threshold:
+ value: 2
+ required: false
+ interval_in_seconds:
+ value: 5
+ required: false
RULES:
- idle_timeout_in_minutes: 30
- enable_floating_ip: true
- enable_tcp_reset: false
+ idle_timeout_in_minutes:
+ value: 30
+ required: false
+ enable_floating_ip:
+ value: true
+ required: false
+ enable_tcp_reset:
+ value: false
+ required: false
diff --git a/src/templates/report.html b/src/templates/report.html
index b4a77595..c5410a96 100644
--- a/src/templates/report.html
+++ b/src/templates/report.html
@@ -111,7 +111,7 @@
.pie {
--passed: #2a882e; /* Adjusted green to be brighter */
--failed: #df3d3d; /* Kept red but slightly adjusted */
- --warning: #fcd116;
+ --warning: #d36325;
--size: 150px;
--thickness: 12px;
--progress: 0;
@@ -193,7 +193,7 @@
}
.test-case.warning .title {
- background-color: #fcd116;
+ background-color: #d36325;
color: white;
}
diff --git a/src/vars/input-api.yaml b/src/vars/input-api.yaml
index 41e0d6b0..58e7a73c 100644
--- a/src/vars/input-api.yaml
+++ b/src/vars/input-api.yaml
@@ -29,7 +29,7 @@ test_groups:
including Corosync settings, Pacemaker resources, SBD device configuration,
and HANA system replication setup. This test is run in an offline mode where the CIB files are
already available in the offline_validation directory.
- enabled: false
+ enabled: true
- name: Azure Load Balancer Validation
task_name: azure-lb
@@ -156,7 +156,7 @@ test_groups:
including Corosync settings, Pacemaker resources, SBD device configuration, and SCS system
replication setup. This test is run in an offline mode where the CIB files are
already available in the offline_validation directory.
- enabled: false
+ enabled: true
- name: Azure Load Balancer Validation
task_name: azure-lb
diff --git a/tests/module_utils/get_pcmk_properties_test.py b/tests/module_utils/get_pcmk_properties_test.py
index ea96dcbe..02b28315 100644
--- a/tests/module_utils/get_pcmk_properties_test.py
+++ b/tests/module_utils/get_pcmk_properties_test.py
@@ -88,53 +88,74 @@
DUMMY_CONSTANTS = {
"VALID_CONFIGS": {
- "REDHAT": {"stonith-enabled": "true", "cluster-name": "hdb_HDB"},
- "azure-fence-agent": {"priority": "10"},
- "sbd": {"pcmk_delay_max": "30"},
+ "REDHAT": {
+ "stonith-enabled": {"value": "true", "required": False},
+ "cluster-name": {"value": "hdb_HDB", "required": False},
+ },
+ "azure-fence-agent": {"priority": {"value": "10", "required": False}},
+ "sbd": {"pcmk_delay_max": {"value": "30", "required": False}},
},
"RSC_DEFAULTS": {
- "resource-stickiness": "1000",
- "migration-threshold": "5000",
+ "resource-stickiness": {"value": "1000", "required": False},
+ "migration-threshold": {"value": "5000", "required": False},
},
"OP_DEFAULTS": {
- "timeout": "600",
- "record-pending": "true",
+ "timeout": {"value": "600", "required": False},
+ "record-pending": {"value": "true", "required": False},
},
"CRM_CONFIG_DEFAULTS": {
- "stonith-enabled": "true",
- "maintenance-mode": "false",
+ "stonith-enabled": {"value": "true", "required": False},
+ "maintenance-mode": {"value": "false", "required": False},
},
"RESOURCE_DEFAULTS": {
"REDHAT": {
"fence_agent": {
- "meta_attributes": {"pcmk_delay_max": "15", "target-role": "Started"},
+ "meta_attributes": {
+ "pcmk_delay_max": {"value": "15", "required": False},
+ "target-role": {"value": "Started", "required": False},
+ },
"operations": {
- "monitor": {"timeout": ["700", "700s"], "interval": "10"},
- "start": {"timeout": "20"},
+ "monitor": {
+ "timeout": {"value": ["700", "700s"], "required": False},
+ "interval": {"value": "10", "required": False},
+ },
+ "start": {"timeout": {"value": "20", "required": False}},
},
- "instance_attributes": {"login": "testuser"},
+ "instance_attributes": {"login": {"value": "testuser", "required": False}},
},
"sbd_stonith": {
- "meta_attributes": {"pcmk_delay_max": "30", "target-role": "Started"},
+ "meta_attributes": {
+ "pcmk_delay_max": {"value": "30", "required": False},
+ "target-role": {"value": "Started", "required": False},
+ },
"operations": {
- "monitor": {"timeout": ["30", "30s"], "interval": "10"},
- "start": {"timeout": "20"},
+ "monitor": {
+ "timeout": {"value": ["30", "30s"], "required": False},
+ "interval": {"value": "10", "required": False},
+ },
+ "start": {"timeout": {"value": "20", "required": False}},
},
},
"test_resource": {
- "meta_attributes": {"clone-max": "2"},
- "operations": {"monitor": {"timeout": ["600", "600s"]}},
- "instance_attributes": {"SID": "HDB"},
+ "meta_attributes": {"clone-max": {"value": "2", "required": False}},
+ "operations": {
+ "monitor": {"timeout": {"value": ["600", "600s"], "required": False}}
+ },
+ "instance_attributes": {"SID": {"value": "HDB", "required": False}},
},
}
},
"OS_PARAMETERS": {
- "DEFAULTS": {"sysctl": {"kernel.numa_balancing": "kernel.numa_balancing = 0"}}
+ "DEFAULTS": {
+ "sysctl": {
+ "kernel.numa_balancing": {"value": "kernel.numa_balancing = 0", "required": False}
+ }
+ }
},
"CONSTRAINTS": {
- "rsc_location": {"score": "INFINITY"},
- "rsc_colocation": {"score": "4000"},
- "rsc_order": {"kind": "Optional"},
+ "rsc_location": {"score": {"value": "INFINITY", "required": False}},
+ "rsc_colocation": {"score": {"value": "4000", "required": False}},
+ "rsc_order": {"kind": {"value": "Optional", "required": False}},
},
}
@@ -254,7 +275,7 @@ def test_get_expected_value_fence_config(self, validator):
"""
validator.fencing_mechanism = "azure-fence-agent"
expected = validator._get_expected_value("crm_config", "priority")
- assert expected == "10"
+ assert expected == ("10", False)
def test_get_resource_expected_value_instance_attributes(self, validator):
"""
@@ -263,7 +284,7 @@ def test_get_resource_expected_value_instance_attributes(self, validator):
expected = validator._get_resource_expected_value(
"fence_agent", "instance_attributes", "login"
)
- assert expected == "testuser"
+ assert expected == ("testuser", False)
def test_get_resource_expected_value_invalid_section(self, validator):
"""
@@ -307,30 +328,16 @@ def test_determine_parameter_status_success_string(self, validator):
"""
Test _determine_parameter_status method with matching string values.
"""
- status = validator._determine_parameter_status("true", "true")
+ status = validator._determine_parameter_status("true", ("true", False))
assert status == TestStatus.SUCCESS.value
def test_determine_parameter_status_error_string(self, validator):
"""
Test _determine_parameter_status method with non-matching string values.
"""
- status = validator._determine_parameter_status("true", "false")
+ status = validator._determine_parameter_status("true", ("false", False))
assert status == TestStatus.ERROR.value
- def test_parse_basic_config(self, validator):
- """
- Test _parse_basic_config method.
- """
- xml_str = """
-
-
- """
- params = validator._parse_basic_config(
- ET.fromstring(xml_str), "crm_config", "test_subcategory"
- )
- assert len(params) == 2
- assert params[0]["category"] == "crm_config_test_subcategory"
-
def test_parse_resource_with_operations(self, validator):
"""
Test _parse_resource method with operations.
@@ -347,25 +354,6 @@ def test_parse_resource_with_operations(self, validator):
assert len(timeout_params) == 2
assert len(interval_params) == 2
- def test_parse_constraints(self, validator):
- """
- Test _parse_constraints method.
- """
- xml_str = """
-
-
-
-
- """
- root = ET.fromstring(xml_str)
- params = validator._parse_constraints(root)
- location_params = [p for p in params if "rsc_location" in p["category"]]
- colocation_params = [p for p in params if "rsc_colocation" in p["category"]]
- order_params = [p for p in params if "rsc_order" in p["category"]]
- assert len(location_params) >= 1
- assert len(colocation_params) >= 1
- assert len(order_params) >= 1
-
def test_parse_resources_section(self, validator):
"""
Test _parse_resources_section method.
@@ -410,15 +398,6 @@ def test_get_scope_from_cib_without_cib_output(self, validator):
scope_element = validator._get_scope_from_cib("resources")
assert scope_element is None
- def test_parse_ha_cluster_config_with_cib(self, validator_with_cib):
- """
- Test parse_ha_cluster_config method with CIB output.
- """
- validator_with_cib.parse_ha_cluster_config()
- result = validator_with_cib.get_result()
- assert result["status"] in [TestStatus.SUCCESS.value, TestStatus.ERROR.value]
- assert "parameters" in result["details"]
-
def test_get_expected_value_for_category_resource(self, validator):
"""
Test _get_expected_value_for_category method for resource category.
@@ -426,7 +405,7 @@ def test_get_expected_value_for_category_resource(self, validator):
expected = validator._get_expected_value_for_category(
"fence_agent", "meta_attributes", "pcmk_delay_max", None
)
- assert expected == "15"
+ assert expected == ("15", False)
def test_get_expected_value_for_category_basic(self, validator):
"""
@@ -435,26 +414,14 @@ def test_get_expected_value_for_category_basic(self, validator):
expected = validator._get_expected_value_for_category(
"crm_config", None, "stonith-enabled", None
)
- assert expected == "true"
+ assert expected == ("true", False)
- def test_determine_parameter_status_error_invalid_expected(self, validator):
+ def test_determine_parameter_status_with_required_parameter(self, validator):
"""
- Test _determine_parameter_status method with invalid expected value type.
+ Test _determine_parameter_status method with required parameter.
"""
- status = validator._determine_parameter_status("value", {"invalid": "dict"})
- assert status == TestStatus.ERROR.value
-
- def test_parse_constraints_skip_missing_attributes(self, validator):
- """
- Test _parse_constraints method skips elements with missing attributes.
- """
- xml_str = """
-
- """
- root = ET.fromstring(xml_str)
- params = validator._parse_constraints(root)
- score_params = [p for p in params if p["name"] == "score"]
- assert len(score_params) == 0
+ status = validator._determine_parameter_status("", ("expected_value", True))
+ assert status == TestStatus.WARNING.value
def test_get_scope_from_cib_invalid_scope(self, validator_with_cib):
"""
diff --git a/tests/modules/get_pcmk_properties_db_test.py b/tests/modules/get_pcmk_properties_db_test.py
index 135b7b7b..bdd75f7e 100644
--- a/tests/modules/get_pcmk_properties_db_test.py
+++ b/tests/modules/get_pcmk_properties_db_test.py
@@ -146,47 +146,90 @@
DUMMY_CONSTANTS = {
"VALID_CONFIGS": {
- "REDHAT": {"stonith-enabled": "true"},
- "azure-fence-agent": {"priority": "10"},
+ "REDHAT": {
+ "stonith-enabled": {"value": "true", "required": False},
+ "cluster-name": {"value": "hdb_HDB", "required": False},
+ },
+ "azure-fence-agent": {"priority": {"value": "10", "required": False}},
+ "sbd": {"pcmk_delay_max": {"value": "30", "required": False}},
},
"RSC_DEFAULTS": {
- "resource-stickiness": "1000",
- "migration-threshold": "5000",
+ "resource-stickiness": {"value": "1000", "required": False},
+ "migration-threshold": {"value": "5000", "required": False},
},
"OP_DEFAULTS": {
- "timeout": "600",
- "record-pending": "true",
+ "timeout": {"value": "600", "required": False},
+ "record-pending": {"value": "true", "required": False},
+ },
+ "CRM_CONFIG_DEFAULTS": {
+ "stonith-enabled": {"value": "true", "required": False},
+ "maintenance-mode": {"value": "false", "required": False},
},
- "CRM_CONFIG_DEFAULTS": {"stonith-enabled": "true"},
"RESOURCE_DEFAULTS": {
"REDHAT": {
"fence_agent": {
- "meta_attributes": {"pcmk_delay_max": "15"},
- "operations": {"monitor": {"timeout": ["700", "700s"]}},
+ "meta_attributes": {
+ "pcmk_delay_max": {"value": "15", "required": False},
+ "target-role": {"value": "Started", "required": False},
+ },
+ "operations": {
+ "monitor": {
+ "timeout": {"value": ["700", "700s"], "required": False},
+ "interval": {"value": "10", "required": False},
+ },
+ "start": {"timeout": {"value": "20", "required": False}},
+ },
+ "instance_attributes": {"login": {"value": "testuser", "required": False}},
},
"sbd_stonith": {
- "meta_attributes": {"pcmk_delay_max": "15"},
- "operations": {"monitor": {"timeout": ["30", "30s"]}},
+ "meta_attributes": {
+ "pcmk_delay_max": {"value": "30", "required": False},
+ "target-role": {"value": "Started", "required": False},
+ },
+ "operations": {
+ "monitor": {
+ "timeout": {"value": ["30", "30s"], "required": False},
+ "interval": {"value": "10", "required": False},
+ },
+ "start": {"timeout": {"value": "20", "required": False}},
+ },
+ },
+ "hana": {
+ "meta_attributes": {"clone-max": {"value": "2", "required": False}},
+ "operations": {
+ "monitor": {"timeout": {"value": ["600", "600s"], "required": False}}
+ },
+ "instance_attributes": {"SID": {"value": "HDB", "required": False}},
},
- "hana": {"meta_attributes": {"clone-max": "2"}},
}
},
"OS_PARAMETERS": {
- "DEFAULTS": {"sysctl": {"kernel.numa_balancing": "kernel.numa_balancing = 0"}}
+ "DEFAULTS": {
+ "sysctl": {
+ "kernel.numa_balancing": {"value": "kernel.numa_balancing = 0", "required": False}
+ }
+ }
},
"GLOBAL_INI": {
"REDHAT": {
"SAPHanaSR": {
- "provider": "SAPHanaSR",
- "path": "/usr/share/SAPHanaSR",
- "execution_order": ["1", "2"],
+ "provider": {"value": "SAPHanaSR", "required": False},
+ "path": {"value": "/usr/share/SAPHanaSR", "required": False},
+ "execution_order": {"value": ["1", "2"], "required": False},
}
},
"SUSE": {
- "SAPHanaSR-angi": {"provider": "SAPHanaSR-angi", "path": "/usr/share/SAPHanaSR-angi"}
+ "SAPHanaSR-angi": {
+ "provider": {"value": "SAPHanaSR-angi", "required": False},
+ "path": {"value": "/usr/share/SAPHanaSR-angi", "required": False},
+ }
},
},
- "CONSTRAINTS": {"rsc_location": {"score": "INFINITY"}},
+ "CONSTRAINTS": {
+ "rsc_location": {"score": {"value": "INFINITY", "required": False}},
+ "rsc_colocation": {"score": {"value": "4000", "required": False}},
+ "rsc_order": {"kind": {"value": "Optional", "required": False}},
+ },
}
@@ -552,26 +595,13 @@ def test_get_expected_value_methods(self, validator):
"""
validator.fencing_mechanism = "azure-fence-agent"
expected = validator._get_expected_value("crm_config", "priority")
- assert expected == "10"
+ assert expected == ("10", False)
expected = validator._get_expected_value("crm_config", "stonith-enabled")
- assert expected == "true"
+ assert expected == ("true", False)
expected = validator._get_resource_expected_value(
"fence_agent", "meta_attributes", "pcmk_delay_max"
)
- assert expected == "15"
-
- def test_parse_constraints_with_valid_constraints(self, validator):
- """
- Test _parse_constraints method with valid constraints.
- """
- xml_str = """
-
-
-
- """
- root = ET.fromstring(xml_str)
- params = validator._parse_constraints(root)
- assert len(params) > 0
+ assert expected == ("15", False)
def test_successful_validation_result(self, validator):
"""
diff --git a/tests/modules/get_pcmk_properties_scs_test.py b/tests/modules/get_pcmk_properties_scs_test.py
index a2f542c9..51bac43e 100644
--- a/tests/modules/get_pcmk_properties_scs_test.py
+++ b/tests/modules/get_pcmk_properties_scs_test.py
@@ -125,61 +125,96 @@
DUMMY_CONSTANTS = {
"VALID_CONFIGS": {
- "REDHAT": {"stonith-enabled": "true", "cluster-name": "scs_S4D"},
- "azure-fence-agent": {"priority": "10"},
- "sbd": {"pcmk_delay_max": "30"},
+ "REDHAT": {
+ "stonith-enabled": {"value": "true", "required": False},
+ "cluster-name": {"value": "scs_S4D", "required": False},
+ },
+ "azure-fence-agent": {"priority": {"value": "10", "required": False}},
+ "sbd": {"pcmk_delay_max": {"value": "30", "required": False}},
},
"RSC_DEFAULTS": {
- "resource-stickiness": "1000",
- "migration-threshold": "5000",
+ "resource-stickiness": {"value": "1000", "required": False},
+ "migration-threshold": {"value": "5000", "required": False},
},
"OP_DEFAULTS": {
- "timeout": "600",
- "record-pending": "true",
+ "timeout": {"value": "600", "required": False},
+ "record-pending": {"value": "true", "required": False},
},
"CRM_CONFIG_DEFAULTS": {
- "stonith-enabled": "true",
- "maintenance-mode": "false",
+ "stonith-enabled": {"value": "true", "required": False},
+ "maintenance-mode": {"value": "false", "required": False},
},
"RESOURCE_DEFAULTS": {
"REDHAT": {
"fence_agent": {
- "meta_attributes": {"pcmk_delay_max": "15", "target-role": "Started"},
+ "meta_attributes": {
+ "pcmk_delay_max": {"value": "15", "required": False},
+ "target-role": {"value": "Started", "required": False},
+ },
"operations": {
- "monitor": {"timeout": ["700", "700s"], "interval": "10"},
- "start": {"timeout": "20"},
+ "monitor": {
+ "timeout": {"value": ["700", "700s"], "required": False},
+ "interval": {"value": "10", "required": False},
+ },
+ "start": {"timeout": {"value": "20", "required": False}},
+ },
+ "instance_attributes": {
+ "login": {"value": "testuser", "required": False},
+ "resourceGroup": {"value": "test-rg", "required": False},
},
- "instance_attributes": {"login": "testuser", "resourceGroup": "test-rg"},
},
"sbd_stonith": {
- "meta_attributes": {"pcmk_delay_max": "30", "target-role": "Started"},
+ "meta_attributes": {
+ "pcmk_delay_max": {"value": "30", "required": False},
+ "target-role": {"value": "Started", "required": False},
+ },
"operations": {
- "monitor": {"timeout": ["30", "30s"], "interval": "10"},
- "start": {"timeout": "20"},
+ "monitor": {
+ "timeout": {"value": ["30", "30s"], "required": False},
+ "interval": {"value": "10", "required": False},
+ },
+ "start": {"timeout": {"value": "20", "required": False}},
},
},
"ascs": {
- "meta_attributes": {"target-role": "Started"},
- "operations": {"monitor": {"timeout": ["600", "600s"]}},
- "instance_attributes": {"InstanceName": "S4D_ASCS00_sapascs"},
+ "meta_attributes": {"target-role": {"value": "Started", "required": False}},
+ "operations": {
+ "monitor": {"timeout": {"value": ["600", "600s"], "required": False}}
+ },
+ "instance_attributes": {
+ "InstanceName": {"value": "S4D_ASCS00_sapascs", "required": False}
+ },
},
"ers": {
- "meta_attributes": {"target-role": "Started"},
- "operations": {"monitor": {"timeout": ["600", "600s"]}},
- "instance_attributes": {"InstanceName": "S4D_ERS10_sapers"},
+ "meta_attributes": {"target-role": {"value": "Started", "required": False}},
+ "operations": {
+ "monitor": {"timeout": {"value": ["600", "600s"], "required": False}}
+ },
+ "instance_attributes": {
+ "InstanceName": {"value": "S4D_ERS10_sapers", "required": False}
+ },
},
"ipaddr": {
- "instance_attributes": {"ip": {"AFS": ["10.0.1.100"], "ANF": ["10.0.1.101"]}}
+ "instance_attributes": {
+ "ip": {
+ "value": {"AFS": ["10.0.1.100"], "ANF": ["10.0.1.101"]},
+ "required": False,
+ }
+ }
},
}
},
"OS_PARAMETERS": {
- "DEFAULTS": {"sysctl": {"kernel.numa_balancing": "kernel.numa_balancing = 0"}}
+ "DEFAULTS": {
+ "sysctl": {
+ "kernel.numa_balancing": {"value": "kernel.numa_balancing = 0", "required": False}
+ }
+ }
},
"CONSTRAINTS": {
- "rsc_location": {"score": "INFINITY"},
- "rsc_colocation": {"score": "4000"},
- "rsc_order": {"kind": "Optional"},
+ "rsc_location": {"score": {"value": "INFINITY", "required": False}},
+ "rsc_colocation": {"score": {"value": "4000", "required": False}},
+ "rsc_order": {"kind": {"value": "Optional", "required": False}},
},
}
@@ -303,7 +338,7 @@ def test_get_expected_value_for_category_resource(self, validator):
expected = validator._get_expected_value_for_category(
"fence_agent", "meta_attributes", "pcmk_delay_max", None
)
- assert expected == "15"
+ assert expected == ("15", False)
def test_get_expected_value_for_category_ascs_ers(self, validator):
"""
@@ -312,11 +347,11 @@ def test_get_expected_value_for_category_ascs_ers(self, validator):
expected = validator._get_expected_value_for_category(
"ascs", "meta_attributes", "target-role", None
)
- assert expected == "Started"
+ assert expected == ("Started", False)
expected = validator._get_expected_value_for_category(
"ers", "meta_attributes", "target-role", None
)
- assert expected == "Started"
+ assert expected == ("Started", False)
def test_get_expected_value_for_category_basic(self, validator):
"""
@@ -325,15 +360,16 @@ def test_get_expected_value_for_category_basic(self, validator):
expected = validator._get_expected_value_for_category(
"crm_config", None, "stonith-enabled", None
)
- assert expected == "true"
+ assert expected == ("true", False)
- def test_determine_parameter_status_with_dict_expected_value_anf(self, validator_anf):
+ def test_determine_parameter_status_with_list_expected_value(self, validator):
"""
- Test _determine_parameter_status method with dict expected value and ANF provider.
+ Test _determine_parameter_status method with list expected value.
"""
- status = validator_anf._determine_parameter_status(
- "10.0.1.101", {"AFS": ["10.0.1.100"], "ANF": ["10.0.1.101"]}
+ status = validator._determine_parameter_status(
+ "10.0.1.101", (["10.0.1.100", "10.0.1.101"], False)
)
+ print(f"Actual status: {status}, Expected: {TestStatus.SUCCESS.value}")
assert status == TestStatus.SUCCESS.value
def test_determine_parameter_status_info_cases(self, validator):
@@ -465,25 +501,6 @@ def test_resource_categories_defined(self, validator):
assert category in HAClusterValidator.RESOURCE_CATEGORIES
assert HAClusterValidator.RESOURCE_CATEGORIES[category].startswith(".//")
- def test_parse_constraints_with_location_constraints(self, validator):
- """
- Test _parse_constraints method with location constraints.
- """
- xml_str = """
-
-
-
-
- """
- root = ET.fromstring(xml_str)
- params = validator._parse_constraints(root)
- location_params = [p for p in params if "rsc_location" in p["category"]]
- colocation_params = [p for p in params if "rsc_colocation" in p["category"]]
- order_params = [p for p in params if "rsc_order" in p["category"]]
- assert len(location_params) >= 1
- assert len(colocation_params) >= 1
- assert len(order_params) >= 1
-
def test_successful_validation_result(self, validator):
"""
Test that validator returns proper result structure.
@@ -518,20 +535,20 @@ def test_get_expected_value_methods_coverage(self, validator):
"""
validator.fencing_mechanism = "azure-fence-agent"
expected = validator._get_expected_value("crm_config", "priority")
- assert expected == "10"
+ assert expected == ("10", False)
expected = validator._get_expected_value("crm_config", "stonith-enabled")
- assert expected == "true"
+ assert expected == ("true", False)
expected = validator._get_resource_expected_value(
"fence_agent", "meta_attributes", "pcmk_delay_max"
)
- assert expected == "15"
+ assert expected == ("15", False)
expected = validator._get_resource_expected_value(
"fence_agent", "operations", "timeout", "monitor"
)
- assert expected == ["700", "700s"]
+ assert expected == (["700", "700s"], False)
expected = validator._get_resource_expected_value(
"fence_agent", "instance_attributes", "login"
)
- assert expected == "testuser"
+ assert expected == ("testuser", False)
expected = validator._get_resource_expected_value("fence_agent", "unknown_section", "param")
assert expected is None