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
2 changes: 1 addition & 1 deletion scripts/sap_automation_qa.sh
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ Examples:
# Configuration Checks (requires TEST_TYPE: ConfigurationChecks in vars.yaml)
$0 --extra-vars='{"configuration_test_type":"all"}'
$0 --extra-vars='{"configuration_test_type":"high_availability"}'
$0 --extra-vars='{"configuration_test_type":"Database"}' -vv
$0 --extra-vars='{"configuration_test_type":"Database"}' -v

Available Test Cases for groups:
$0 --test_groups=HA_DB_HANA
Expand Down
20 changes: 13 additions & 7 deletions src/module_utils/collector.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ def collect(self, check, context) -> str:
user = check.collector_args.get("user", "")
if not command:
return "ERROR: No command specified"
if user:
if not re.match(r"^[a-zA-Z0-9_-]+$", user):
self.parent.log(logging.ERROR, f"Invalid user parameter detected: {user}")
return "ERROR: Invalid user parameter"

try:
command = self.sanitize_command(command)
except ValueError as e:
Expand All @@ -123,16 +128,13 @@ def collect(self, check, context) -> str:
)
return f"ERROR: Command sanitization failed after substitution: {e}"

check.command = command
if user and user != "root":
if not re.match(r"^[a-zA-Z0-9_-]+$", user):
self.parent.log(logging.ERROR, f"Invalid user parameter: {user}")
return f"ERROR: Invalid user parameter: {user}"

if user == "db2sid":
user = f"db2{context.get('database_sid', '').lower()}"
command = f"su - {user} -c {shlex.quote(command)}"
self.parent.log(logging.INFO, f"Executing command as user {user} {command}")

command = f"sudo -u {shlex.quote(user)} {command}"
check.command = command

return self.parent.execute_command_subprocess(
command, shell_command=check.collector_args.get("shell", True)
Expand Down Expand Up @@ -339,7 +341,11 @@ def parse_disks_vars(self, check, context) -> str:

fs_entry = None
for fs in filesystem_data:
if fs.get("target") == mount_point:
if fs.get("target") in (
mount_point,
f"{mount_point}/{context.get('database_sid', '').upper()}",
f"{mount_point}/{context.get('sap_sid', '').upper()}",
):
fs_entry = fs
break

Expand Down
46 changes: 41 additions & 5 deletions src/module_utils/filesystem_collector.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,25 @@ def _parse_filesystem_data(
for nfs_share in afs_storage_data:
storage_account_name = nfs_share.get("Pool", "")
share_address = nfs_share.get("NFSAddress", "")
if (
ip_match = (
":" in share_address and share_address.split(":")[0] == nfs_address
) or storage_account_name in nfs_address:
)
fqdn_match = storage_account_name and storage_account_name in nfs_source
ip_to_account_match = False
try:
ipaddress.ip_address(nfs_address)
except Exception as ex:
self.parent.log(
logging.DEBUG,
f"NFS address {nfs_address} is not a valid IP: {ex}",
)
if ":" in nfs_source and "/" in nfs_source:
mount_path = nfs_source.split(":", 1)[1]
ip_to_account_match = (
storage_account_name
and ("/" + storage_account_name + "/") in mount_path
)
if ip_match or fqdn_match or ip_to_account_match:
filesystem_entry["max_mbps"] = nfs_share.get("ThroughputMibps", 0)
filesystem_entry["max_iops"] = nfs_share.get("IOPS", 0)
filesystem_entry["nfs_type"] = "AFS"
Expand Down Expand Up @@ -472,16 +488,36 @@ def gather_all_filesystem_info(
if not matched:
for nfs_share in afs_storage_data:
share_address = nfs_share.get("NFSAddress", "")
if (
storage_account_name = nfs_share.get("Pool", "")
ip_match = (
":" in share_address
and share_address.split(":")[0] == nfs_address
):
)
fqdn_match = storage_account_name and storage_account_name in source
ip_to_account_match = False
try:
ipaddress.ip_address(nfs_address)
if ":" in source and "/" in source:
mount_path = source.split(":", 1)[1]
ip_to_account_match = (
storage_account_name
and ("/" + storage_account_name + "/") in mount_path
)
except ValueError:
pass

if ip_match or fqdn_match or ip_to_account_match:
max_mbps = nfs_share.get("ThroughputMibps", 0)
max_iops = nfs_share.get("IOPS", 0)
match_type = (
"IP"
if ip_match
else ("FQDN" if fqdn_match else "IP→Account")
)
self.parent.log(
logging.INFO,
f"Correlated NFS {target} with "
+ f"AFS: MBPS={max_mbps}, IOPS={max_iops}",
+ f"AFS ({match_type}): MBPS={max_mbps}, IOPS={max_iops}, Account={storage_account_name}",
)
break

Expand Down
2 changes: 1 addition & 1 deletion src/modules/configuration_check_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ def is_check_applicable(self, check: Check) -> bool:
"""
self.log(
logging.DEBUG,
f"Checking applicability for check {check.applicability} with context: {self.context}",
f"Checking applicability for check {check.applicability}",
)
for rule in check.applicability:
context_value = self.context.get(rule.property)
Expand Down
36 changes: 19 additions & 17 deletions src/playbook_00_configuration_checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
msg: "Host {{ inventory_hostname }} assigned role: {{ role }} from groups: {{ group_names }}"

- name: "Load supported VM information"
no_log: true
ansible.builtin.include_vars:
file: "./roles/configuration_checks/vars/vm-support.yml"
name: vm_support
Expand Down Expand Up @@ -258,12 +259,12 @@
'check_type': 'vm_info',
'metadata': hostvars[item].vm_info_results_metadata
| default({})}] }}"
loop: "{{ groups[sap_sid | upper + '_SCS']|default([]) +
loop: "{{ (groups[sap_sid | upper + '_SCS']|default([]) +
groups[sap_sid | upper + '_ERS']|default([]) +
groups[sap_sid | upper + '_DB']|default([]) +
groups[sap_sid | upper + '_APP']|default([]) +
groups[sap_sid | upper + '_WEB']|default([]) +
groups[sap_sid | upper + '_PAS']|default([]) }}"
groups[sap_sid | upper + '_PAS']|default([])) | unique }}"
when: hostvars[item].vm_info_results is defined

- name: "Collect package info check results"
Expand All @@ -275,12 +276,12 @@
'check_type': 'package_info',
'metadata': hostvars[item].package_info_results_metadata
| default({})}] }}"
loop: "{{ groups[sap_sid | upper + '_SCS']|default([]) +
loop: "{{ (groups[sap_sid | upper + '_SCS']|default([]) +
groups[sap_sid | upper + '_ERS']|default([]) +
groups[sap_sid | upper + '_DB']|default([]) +
groups[sap_sid | upper + '_APP']|default([]) +
groups[sap_sid | upper + '_WEB']|default([]) +
groups[sap_sid | upper + '_PAS']|default([]) }}"
groups[sap_sid | upper + '_PAS']|default([])) | unique }}"
when: hostvars[item].package_info_results is defined

- name: "Collect common SAP check results"
Expand All @@ -292,12 +293,12 @@
'check_type': 'common_sap',
'metadata': hostvars[item].common_sap_results_metadata
| default({})}] }}"
loop: "{{ groups[sap_sid | upper + '_SCS']|default([]) +
loop: "{{ (groups[sap_sid | upper + '_SCS']|default([]) +
groups[sap_sid | upper + '_ERS']|default([]) +
groups[sap_sid | upper + '_DB']|default([]) +
groups[sap_sid | upper + '_APP']|default([]) +
groups[sap_sid | upper + '_WEB']|default([]) +
groups[sap_sid | upper + '_PAS']|default([]) }}"
groups[sap_sid | upper + '_PAS']|default([])) | unique }}"
when: hostvars[item].common_sap_results is defined

- name: "Collect networking check results"
Expand All @@ -309,12 +310,12 @@
'check_type': 'networking',
'metadata': hostvars[item].networking_results_metadata
| default({})}] }}"
loop: "{{ groups[sap_sid | upper + '_SCS']|default([]) +
loop: "{{ (groups[sap_sid | upper + '_SCS']|default([]) +
groups[sap_sid | upper + '_ERS']|default([]) +
groups[sap_sid | upper + '_DB']|default([]) +
groups[sap_sid | upper + '_APP']|default([]) +
groups[sap_sid | upper + '_WEB']|default([]) +
groups[sap_sid | upper + '_PAS']|default([]) }}"
groups[sap_sid | upper + '_PAS']|default([])) | unique }}"
when: hostvars[item].networking_results is defined

- name: "Collect DB (HANA) check results"
Expand Down Expand Up @@ -362,8 +363,8 @@
'check_type': 'ascs_scs',
'metadata': hostvars[item].ascs_scs_results_metadata
| default({})}] }}"
loop: "{{ groups[sap_sid | upper + '_SCS']|default([]) +
groups[sap_sid | upper + '_ERS']|default([]) }}"
loop: "{{ (groups[sap_sid | upper + '_SCS']|default([]) +
groups[sap_sid | upper + '_ERS']|default([])) | unique }}"
when: hostvars[item].ascs_scs_results is defined

- name: "Collect SCS/ERS HA configuration check results"
Expand All @@ -375,8 +376,8 @@
'check_type': 'scs_ha_config',
'metadata': hostvars[item].scs_ha_config_results_metadata
| default({})}] }}"
loop: "{{ groups[sap_sid | upper + '_SCS']|default([]) +
groups[sap_sid | upper + '_ERS']|default([]) }}"
loop: "{{ (groups[sap_sid | upper + '_SCS']|default([]) +
groups[sap_sid | upper + '_ERS']|default([])) | unique }}"
when: hostvars[item].scs_ha_config_results is defined

- name: "Collect Application Server check results"
Expand All @@ -388,9 +389,9 @@
'check_type': 'app_server',
'metadata': hostvars[item].app_server_results_metadata
| default({})}] }}"
loop: "{{ groups[sap_sid | upper + '_APP']|default([]) +
loop: "{{ (groups[sap_sid | upper + '_APP']|default([]) +
groups[sap_sid | upper + '_PAS']|default([]) +
groups[sap_sid | upper + '_WEB']|default([]) }}"
groups[sap_sid | upper + '_WEB']|default([])) | unique }}"
when: hostvars[item].app_server_results is defined

- name: "Debug execution metadata"
Expand Down Expand Up @@ -427,12 +428,12 @@
if scs_high_availability
| default(false) | bool
else 'N/A' }}"
hostnames: "{{ groups[sap_sid | upper + '_SCS']|default([]) +
hostnames: "{{ (groups[sap_sid | upper + '_SCS']|default([]) +
groups[sap_sid | upper + '_ERS']|default([]) +
groups[sap_sid | upper + '_DB']|default([]) +
groups[sap_sid | upper + '_APP']|default([]) +
groups[sap_sid | upper + '_WEB']|default([]) +
groups[sap_sid | upper + '_PAS']|default([]) }}"
groups[sap_sid | upper + '_PAS']|default([])) | unique }}"
passed_count: "{{ all_results | selectattr('status', 'equalto', 'PASSED') | list | length }}"
error_count: "{{ all_results | selectattr('status', 'equalto', 'FAILED') | list | length }}"
warning_count: "{{ all_results | selectattr('status', 'equalto', 'WARNING') | list | length }}"
Expand All @@ -441,10 +442,11 @@

- name: "Render HTML report for configuration checks"
ansible.builtin.include_tasks: "./roles/misc/tasks/render-html-report.yml"
no_log: true
vars:
html_template_name: "./templates/config_checks_report.html"
report_file_name: "CONFIG_{{ sap_sid | upper }}_{{ platform | upper }}"

- name: "Debug the file name of the report generated"
ansible.builtin.debug:
msg: "Report file CONFIG_{{ sap_sid | upper }}_{{ platform | upper }} generated."
msg: "Report file CONFIG_{{ sap_sid | upper }}_{{ platform | upper }}_{{ test_group_invocation_id }} generated."
Loading