From f1fe7e8ef052780511e3a7ca66261bdd3e28cb2a Mon Sep 17 00:00:00 2001 From: sean-freeman <1815807+sean-freeman@users.noreply.github.com> Date: Sat, 9 Nov 2024 00:21:49 +0000 Subject: [PATCH 01/26] fix: remove py shebang which cause errors with py venv --- plugins/modules/maintenance_planner_files.py | 2 +- plugins/modules/maintenance_planner_stack_xml_download.py | 2 +- plugins/modules/software_center_download.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/modules/maintenance_planner_files.py b/plugins/modules/maintenance_planner_files.py index 5735920..b52f22f 100644 --- a/plugins/modules/maintenance_planner_files.py +++ b/plugins/modules/maintenance_planner_files.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +# -*- coding: utf-8 -*- # SAP Maintenance Planner files retrieval diff --git a/plugins/modules/maintenance_planner_stack_xml_download.py b/plugins/modules/maintenance_planner_stack_xml_download.py index e7a5182..a08387b 100644 --- a/plugins/modules/maintenance_planner_stack_xml_download.py +++ b/plugins/modules/maintenance_planner_stack_xml_download.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +# -*- coding: utf-8 -*- # SAP Maintenance Planner Stack XML download diff --git a/plugins/modules/software_center_download.py b/plugins/modules/software_center_download.py index 9278572..952af26 100644 --- a/plugins/modules/software_center_download.py +++ b/plugins/modules/software_center_download.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +# -*- coding: utf-8 -*- # SAP software download module From 6590b7f987f1fc2be6a7d7d1d51177339863e16d Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Fri, 21 Mar 2025 16:31:27 +0100 Subject: [PATCH 02/26] feat: query_latest module feature --- ...unchpad_software_center_download_runner.py | 105 +++++++- ...d_software_center_download_search_fuzzy.py | 243 +++++++++++++++++- plugins/modules/software_center_download.py | 29 ++- 3 files changed, 351 insertions(+), 26 deletions(-) diff --git a/plugins/module_utils/sap_launchpad_software_center_download_runner.py b/plugins/module_utils/sap_launchpad_software_center_download_runner.py index 86cc55b..9f2c288 100644 --- a/plugins/module_utils/sap_launchpad_software_center_download_runner.py +++ b/plugins/module_utils/sap_launchpad_software_center_download_runner.py @@ -10,6 +10,7 @@ from . import constants as C from .sap_api_common import _request, https_session from .sap_id_sso import _get_sso_endpoint_meta +from .sap_launchpad_software_center_download_search_fuzzy import * logger = logging.getLogger(__name__) @@ -17,27 +18,78 @@ MAX_RETRY_TIMES = 3 -def search_software_filename(name, deduplicate): - """Return a single software that matched the filename +def search_software_filename(name, deduplicate, latest): """ - search_results = _search_software(name) - softwares = [r for r in search_results if r['Title'] == name or r['Description'] == name] - num_files=len(softwares) + Execute search for SAP Software or its alternative when query_latest is true. + + Args: + name: The filename name to check (e.g. 'SAPCAR_1115-70006178.EXE'). + deduplicate + latest + + Returns: + download_link of matched SAP Software. + filename of matched SAP Software. + latest_found if latest search was successful. + """ + + latest_found = False + software_search = _search_software(name) + software_filtered = [r for r in software_search if r['Title'] == name or r['Description'] == name] + + num_files=len(software_filtered) if num_files == 0: - raise ValueError(f'no result found for {name}') + # Run fuzzy search if query_latest was selected + if latest: + software_fuzzy_found = search_software_fuzzy(name) + software_fuzzy_filtered, suggested_filename = filter_fuzzy_search(software_fuzzy_found, name) + if len(software_fuzzy_filtered) == 0: + raise ValueError(f'File {name} is not available to download and has no alternatives') + + software_fuzzy_latest = software_fuzzy_filtered[0].get('Title') + + # software_search_latest = _search_software(software_fuzzy_latest) + # Search has to be filtered again, because API call can get + # duplicates like 70SWPM10SP43_2-20009701.sar for SWPM10SP43_2-20009701.SAR + software_search_latest = _search_software(software_fuzzy_latest) + software_search_latest_filtered = [ + file for file in software_search_latest + if file.get('Title', '').startswith(suggested_filename) + ] + num_files_latest=len(software_search_latest_filtered) + if num_files_latest == 0: + raise ValueError(f'File {name} is not available to download and has no alternatives') + elif num_files_latest > 1 and deduplicate == '': + names = [s['Title'] for s in software_search_latest_filtered] + raise ValueError('More than one results were found: %s. ' + 'please use the correct full filename' % names) + elif num_files_latest > 1 and deduplicate == 'first': + software_found = software_search_latest_filtered[0] + latest_found = True + elif num_files_latest > 1 and deduplicate == 'last': + software_found = software_search_latest_filtered[num_files-1] + latest_found = True + else: + software_found = software_search_latest_filtered[0] + latest_found = True + else: + raise ValueError(f'File {name} is not available to download') + elif num_files > 1 and deduplicate == '': - names = [s['Title'] for s in softwares] - raise ValueError('more than one results were found: %s. ' + names = [s['Title'] for s in software_filtered] + raise ValueError('More than one results were found: %s. ' 'please use the correct full filename' % names) elif num_files > 1 and deduplicate == 'first': - software = softwares[0] + software_found = software_filtered[0] elif num_files > 1 and deduplicate == 'last': - software = softwares[num_files-1] + software_found = software_filtered[num_files-1] else: - software = softwares[0] + software_found = software_filtered[0] - download_link, filename = software['DownloadDirectLink'], name - return (download_link, filename) + download_link = software_found['DownloadDirectLink'] + filename = _get_valid_filename(software_found) + + return (download_link, filename, latest_found) def download_software(download_link, filename, output_dir, retry=0): @@ -151,6 +203,7 @@ def download_software_via_legacy_api(username, password, download_link, def _search_software(keyword): + url = C.URL_SOFTWARE_CENTER_SERVICE + '/SearchResultSet' params = { 'SEARCH_MAX_RESULT': 500, @@ -167,7 +220,7 @@ def _search_software(keyword): j = json.loads(res.text) results = j['d']['results'] except json.JSONDecodeError: - # When use has no authority to search some specified softwares, + # When use has no authority to search some specified files, # it will return non-json response, which is actually expected. # So just return an empty list. logger.warning('Non-JSON response returned for software searching') @@ -246,3 +299,27 @@ def _is_checksum_matched(f, etag): for chunk in iter(lambda: f.read(4096 * hash.block_size), b""): hash.update(chunk) return hash.hexdigest() == checksum + + +def _get_valid_filename(software_found): + """ + Ensure that CD Media have correct filenames from description. + Example: S4CORE105_INST_EXPORT_1.zip downloads as 19118000000000004323 + + Args: + software_found: List[0] with dictionary of file. + + Returns: + Valid filename for CD Media files, where applicable. + """ + + # Check if Title contains filename and extension + if re.match(r'^[^/\\\0]+\.[^/\\\0]+$', software_found['Title']): + return software_found['Title'] + else: + # Check if Description contains filename and extension + if re.match(r'^[^/\\\0]+\.[^/\\\0]+$', software_found['Description']): + return software_found['Description'] + else: + # Default to Title if Description does not help + return software_found['Title'] diff --git a/plugins/module_utils/sap_launchpad_software_center_download_search_fuzzy.py b/plugins/module_utils/sap_launchpad_software_center_download_search_fuzzy.py index 98be35f..9fba4cf 100644 --- a/plugins/module_utils/sap_launchpad_software_center_download_search_fuzzy.py +++ b/plugins/module_utils/sap_launchpad_software_center_download_search_fuzzy.py @@ -1,5 +1,7 @@ import csv import logging +import os +import re import requests @@ -8,16 +10,38 @@ def search_software_fuzzy(query, max=None, csv_filename=None): - """Returns a list of dict for the software results. """ - results = _search_software(query) + Execute fuzzy search using Unique Software ID instead of name. + ID is unique to Product and Platform combination. + Example of shared ID 80002616: + - SYBCTRL_1440-80002616.SAR + - SYBCTRL_1436-80002616.SAR + + Args: + query: The filename name to check (e.g. 'SYBCTRL_1440-80002616.SAR'). + + Returns: + The list of dict for the software results. + Empty list is returned if query does not contain ID. + """ + # Format query to split filename. + filename_base = os.path.splitext(query)[0] # Remove extension + + # Ensure that fuzzy search is run only for valid IDs. + # This excludes unique files without ID like: S4CORE105_INST_EXPORT_1.zip + if '-' in filename_base: + filename_id = filename_base.split('-')[-1] # Split id from filename + else: + return [] + + results = _search_software(filename_id) num = 0 - softwares = [] + fuzzy_results = [] while True: for r in results: r = _remove_useless_keys(r) - softwares.append(r) + fuzzy_results.append(r) num += len(results) # quit if no results or results number reach the max if num == 0 or (max and num >= max): @@ -35,9 +59,216 @@ def search_software_fuzzy(query, max=None, csv_filename=None): break if csv_filename: - _write_software_results(softwares, csv_filename) + _write_software_results(fuzzy_results, csv_filename) return - return softwares + return fuzzy_results + + +def filter_fuzzy_search(fuzzy_results, filename): + """ + Filter fuzzy search output using filename. + + Args: + fuzzy_results: Output of search_software_fuzzy. + filename: The filename name to check + + Returns: + fuzzy_results_sorted: The list of files that match the filter criteria, sorted by 'Title' in descending order. + suggested_filename: Return generated keyword for further reuse after API call. + """ + + # Prepare filtered list for specific SPS + suggested_filename = _prepare_search_filename_specific(filename) + + fuzzy_results_filtered = [ + file for file in fuzzy_results + if file.get('Title', '').startswith(suggested_filename) + ] + + # Repeat filtering without specific SPS + if len(fuzzy_results_filtered) == 0: + suggested_filename = _prepare_search_filename_nonspecific(filename) + + fuzzy_results_filtered = [ + file for file in fuzzy_results + if file.get('Title', '').startswith(suggested_filename) + ] + + # fuzzy_results_sorted = sorted(fuzzy_results_filtered, key=lambda item: item.get('Title', ''), reverse=True) + fuzzy_results_sorted =_sort_fuzzy_results(fuzzy_results_filtered, filename) + + return fuzzy_results_sorted, suggested_filename + + +def _prepare_search_filename_specific(filename): + """ + Prepare suggested search keyword for known products specific to SPS version. + + Args: + filename: The filename name to check + + Returns: + Suggested filename to filter fuzzy search. + """ + + # Format query to split filename. + filename_base = os.path.splitext(filename)[0] # Remove extension + filename_name = filename_base.rsplit('_', 1)[0] # Split software name from version + # Following filenames will be processed using default filename_name split. + # Return SYBCTRL for SYBCTRL_1436-80002616.SAR + # Return SMDA720 for SMDA720_SP11_22-80003641.SAR + + + for swpm_version in ("70SWPM1", "70SWPM2", "SWPM1", "SWPM2"): + if filename_base.startswith(swpm_version): + return swpm_version + + # Return SUM11SP04 for SUM11SP04_2-80006858.SAR + if filename_base.startswith('SUM'): + return filename.split('-')[0].split('_')[0] + + # Return DBATL740O11 for DBATL740O11_48-80002605.SAR + elif filename_base.startswith('DBATL'): + return filename.split('-')[0].split('_')[0] + + # Return IMDB_AFL20_077 for IMDB_AFL20_077_0-80002045.SAR + # Return IMDB_AFL100_102P for IMDB_AFL100_102P_41-10012328.SAR + elif filename_base.startswith('IMDB_AFL'): + return "_".join(filename.split('-')[0].split('_')[:3]) + + # Return IMDB_CLIENT20_021 for IMDB_CLIENT20_021_31-80002082.SAR + elif filename_base.startswith('IMDB_CLIENT'): + return "_".join(filename.split('-')[0].split('_')[:3]) + + # IMDB_LCAPPS for SAP HANA 1.0 + # Return IMDB_LCAPPS_122 for IMDB_LCAPPS_122P_3300-20010426.SAR + elif filename_base.startswith('IMDB_LCAPPS_1'): + filename_parts = filename.split('-')[0].rsplit('_', 2) + return f"{filename_parts[0]}_{filename_parts[1][:3]}" + + # IMDB_LCAPPS for SAP HANA 2.0 + # Return IMDB_LCAPPS_206 for IMDB_LCAPPS_2067P_400-80002183.SAR + elif filename_base.startswith('IMDB_LCAPPS_2'): + filename_parts = filename.split('-')[0].rsplit('_', 2) + return f"{filename_parts[0]}_{filename_parts[1][:3]}" + + # Return IMDB_SERVER20_06 (SPS06) for IMDB_SERVER20_067_4-80002046.SAR + elif filename_base.startswith('IMDB_SERVER'): + filename_parts = filename.split('-')[0].rsplit('_', 2) + return f"{filename_parts[0]}_{filename_parts[1][:2]}" + + # Return SAPEXE_100 for SAPEXE_100-80005374.SAR + elif filename_base.startswith('SAPEXE'): + return filename_base.split('-')[0] + + # Return SAPHANACOCKPIT02 (SPS02) for SAPHANACOCKPIT02_0-70002300.SAR + elif filename_base.startswith('SAPHANACOCKPIT'): + return filename_base.split('-')[0].rsplit('_', 1)[0] + + # Return unchanged filename_name + else: + return filename_name + + +def _prepare_search_filename_nonspecific(filename): + """ + Prepare suggested search keyword for known products nonspecific to SPS version. + + Args: + filename: The filename name to check + + Returns: + Suggested filename to filter fuzzy search. + """ + + # Format query to split filename. + filename_base = os.path.splitext(filename)[0] # Remove extension + filename_name = filename_base.rsplit('_', 1)[0] # Split software name from version + + # Return SUM11 for SUM11SP04_2-80006858.SAR + if filename_base.startswith('SUM'): + if filename_base.startswith('SUMHANA'): + return 'SUMHANA' + elif filename_base[3:5].isdigit(): # Allow only SUM and 2 digits + return filename_base[:5] + + # Return DBATL740O11 for DBATL740O11_48-80002605.SAR + elif filename_base.startswith('DBATL'): + return filename.split('-')[0].split('_')[0] + + # Return IMDB_AFL20 for IMDB_AFL20_077_0-80002045.SAR + # Return IMDB_AFL100 for IMDB_AFL100_102P_41-10012328.SAR + elif filename_base.startswith('IMDB_AFL'): + return "_".join(filename.split('-')[0].split('_')[:2]) + + # Return IMDB_CLIENT for IMDB_CLIENT20_021_31-80002082.SAR + elif filename_base.startswith('IMDB_CLIENT'): + return 'IMDB_CLIENT' + + # Return IMDB_LCAPPS for IMDB_LCAPPS_122P_3300-20010426.SAR + elif filename_base.startswith('IMDB_LCAPPS'): + return "_".join(filename.split('-')[0].split('_')[:2]) + + # Return IMDB_SERVER20 for IMDB_SERVER20_067_4-80002046.SAR + elif filename_base.startswith('IMDB_SERVER'): + return "_".join(filename.split('-')[0].split('_')[:2]) + + # Return SAPHANACOCKPIT for SAPHANACOCKPIT02_0-70002300.SAR + elif filename_base.startswith('SAPHANACOCKPIT'): + return 'SAPHANACOCKPIT' + + # Return SAPHOSTAGENT for SAPHOSTAGENT61_61-80004831.SAR + elif filename_base.startswith('SAPHOSTAGENT'): + return 'SAPHOSTAGENT' + + # Return unchanged filename_name + else: + return filename_name + + +def _sort_fuzzy_results(fuzzy_results_filtered, filename): + """ + Sort results of fuzzy search for known nonstandard versions. + Example: + IMDB_LCAPPS_122P_3500-20010426.SAR, IMDB_LCAPPS_122P_600-70001332.SAR + + Args: + fuzzy_results_filtered: The list of filtered fuzzy results. + filename: The filename name to check. + + Returns: + Ordered list of fuzzy results, based on known nonstandard versions. + """ + + if _get_numeric_search_keyword(filename): + software_fuzzy_sorted = sorted( + fuzzy_results_filtered, + key= lambda item: _get_numeric_search_keyword(item.get('Title', '')), + reverse=True, + ) + else: + software_fuzzy_sorted = sorted( + fuzzy_results_filtered, + key=lambda item: item.get('Title', ''), + reverse=True, + ) + + return software_fuzzy_sorted + + +def _get_numeric_search_keyword(filename): + """ + Extract integer value of version from filename. + + Args: + filename: The filename name to check. + + """ + match = re.search(r'_(\d+)-', filename) + if match: + return int(match.group(1)) + else: + return None def _search_software(keyword, remove_useless_keys=False): diff --git a/plugins/modules/software_center_download.py b/plugins/modules/software_center_download.py index 952af26..9ea4091 100644 --- a/plugins/modules/software_center_download.py +++ b/plugins/modules/software_center_download.py @@ -50,6 +50,11 @@ - How to handle multiple search results. required: false type: str + query_latest: + description: + - Enable search for alternative packages, when filename is not available. + required: false + type: bool author: - Lab for SAP Solutions @@ -102,7 +107,8 @@ def run_module(): download_filename=dict(type='str', required=False, default=''), dest=dict(type='str', required=True), dry_run=dict(type='bool', required=False, default=False), - deduplicate=dict(type='str', required=False, default='') + deduplicate=dict(type='str', required=False, default=''), + query_latest=dict(type='bool', required=False, default=False) ) # Define result dictionary objects to be passed back to Ansible @@ -130,6 +136,8 @@ def run_module(): dest = module.params.get('dest') dry_run = module.params.get('dry_run') deduplicate = module.params.get('deduplicate') + latest = module.params.get('query_latest') + # Main run @@ -140,15 +148,21 @@ def run_module(): pattern = dest + '/**/' + os.path.splitext(filename)[0] + '*' for file in glob.glob(pattern, recursive=True): if os.path.exists(file): - module.exit_json(skipped=True, msg="file {} already exists".format(file)) + module.exit_json(skipped=True, msg=f"File '{os.path.basename(file)}' already exists") # Initiate login with given credentials sap_sso_login(username, password) - # EXEC: query - # execute search_software_filename first to get download link and filename + # Execute search_software_filename first to get download link and filename + download_latest = False # True if query_latest was successful if query: - download_link, download_filename = search_software_filename(query,deduplicate) + download_link, download_filename, download_latest = search_software_filename(query,deduplicate,latest) + # Recheck file availability if query_latest is used + if latest: + pattern = dest + '/**/' + os.path.splitext(download_filename)[0] + '*' + for file in glob.glob(pattern, recursive=True): + if os.path.exists(file): + module.exit_json(skipped=True, msg=f"Alternative file '{os.path.basename(file)}' already exists - original file '{query}' is not available to download") # execute download_software if dry_run: @@ -162,7 +176,10 @@ def run_module(): # Process return dictionary for Ansible result['changed'] = True - result['msg'] = "SAP software download successful" + if query and download_latest: + result['msg'] = f"Successfully downloaded alternative SAP software: {download_filename} - original file '{query}' is not available to download" + else: + result['msg'] = f"Successfully downloaded SAP software: {download_filename}" except requests.exceptions.HTTPError as e: # module.fail_json(msg='SAP SSO authentication failed' + str(e), **result) From b9392cd614c54ba2b2f086f880e3538cd8ac6630 Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Tue, 25 Mar 2025 10:28:36 +0100 Subject: [PATCH 03/26] feat: enhanced dry_run, updated naming of variables and parameters --- ...unchpad_software_center_download_runner.py | 67 +++++++++---------- plugins/modules/software_center_download.py | 56 ++++++++++++---- 2 files changed, 75 insertions(+), 48 deletions(-) diff --git a/plugins/module_utils/sap_launchpad_software_center_download_runner.py b/plugins/module_utils/sap_launchpad_software_center_download_runner.py index 9f2c288..241ab71 100644 --- a/plugins/module_utils/sap_launchpad_software_center_download_runner.py +++ b/plugins/module_utils/sap_launchpad_software_center_download_runner.py @@ -18,78 +18,77 @@ MAX_RETRY_TIMES = 3 -def search_software_filename(name, deduplicate, latest): +def search_software_filename(name, deduplicate, search_alternatives): """ - Execute search for SAP Software or its alternative when query_latest is true. + Execute search for SAP Software or its alternative when search_alternatives is true. Args: name: The filename name to check (e.g. 'SAPCAR_1115-70006178.EXE'). - deduplicate - latest + deduplicate: Select deduplication logic from 'first', 'last' + search_alternatives: Boolean for enabling fuzzy search. Returns: - download_link of matched SAP Software. - filename of matched SAP Software. - latest_found if latest search was successful. + download_link: Download link of matched SAP Software. + filename: File name of matched SAP Software. + alternative_found: True if alternative search was successful. """ - latest_found = False + alternative_found = False software_search = _search_software(name) software_filtered = [r for r in software_search if r['Title'] == name or r['Description'] == name] - num_files=len(software_filtered) - if num_files == 0: - # Run fuzzy search if query_latest was selected - if latest: + files_count=len(software_filtered) + if files_count == 0: + # Run fuzzy search if search_alternatives was selected + if search_alternatives: software_fuzzy_found = search_software_fuzzy(name) software_fuzzy_filtered, suggested_filename = filter_fuzzy_search(software_fuzzy_found, name) if len(software_fuzzy_filtered) == 0: raise ValueError(f'File {name} is not available to download and has no alternatives') - software_fuzzy_latest = software_fuzzy_filtered[0].get('Title') + software_fuzzy_alternatives = software_fuzzy_filtered[0].get('Title') - # software_search_latest = _search_software(software_fuzzy_latest) # Search has to be filtered again, because API call can get # duplicates like 70SWPM10SP43_2-20009701.sar for SWPM10SP43_2-20009701.SAR - software_search_latest = _search_software(software_fuzzy_latest) - software_search_latest_filtered = [ - file for file in software_search_latest + software_search_alternatives = _search_software(software_fuzzy_alternatives) + software_search_alternatives_filtered = [ + file for file in software_search_alternatives if file.get('Title', '').startswith(suggested_filename) ] - num_files_latest=len(software_search_latest_filtered) - if num_files_latest == 0: + alternatives_count=len(software_search_alternatives_filtered) + if alternatives_count == 0: raise ValueError(f'File {name} is not available to download and has no alternatives') - elif num_files_latest > 1 and deduplicate == '': - names = [s['Title'] for s in software_search_latest_filtered] + elif alternatives_count > 1 and deduplicate == '': + names = [s['Title'] for s in software_search_alternatives_filtered] raise ValueError('More than one results were found: %s. ' 'please use the correct full filename' % names) - elif num_files_latest > 1 and deduplicate == 'first': - software_found = software_search_latest_filtered[0] - latest_found = True - elif num_files_latest > 1 and deduplicate == 'last': - software_found = software_search_latest_filtered[num_files-1] - latest_found = True + elif alternatives_count > 1 and deduplicate == 'first': + software_found = software_search_alternatives_filtered[0] + alternative_found = True + elif alternatives_count > 1 and deduplicate == 'last': + software_found = software_search_alternatives_filtered[alternatives_count-1] + alternative_found = True else: - software_found = software_search_latest_filtered[0] - latest_found = True + software_found = software_search_alternatives_filtered[0] + alternative_found = True else: raise ValueError(f'File {name} is not available to download') - elif num_files > 1 and deduplicate == '': + elif files_count > 1 and deduplicate == '': names = [s['Title'] for s in software_filtered] raise ValueError('More than one results were found: %s. ' 'please use the correct full filename' % names) - elif num_files > 1 and deduplicate == 'first': + elif files_count > 1 and deduplicate == 'first': software_found = software_filtered[0] - elif num_files > 1 and deduplicate == 'last': - software_found = software_filtered[num_files-1] + elif files_count > 1 and deduplicate == 'last': + software_found = software_filtered[files_count-1] else: software_found = software_filtered[0] download_link = software_found['DownloadDirectLink'] filename = _get_valid_filename(software_found) - return (download_link, filename, latest_found) + return (download_link, filename, alternative_found) def download_software(download_link, filename, output_dir, retry=0): diff --git a/plugins/modules/software_center_download.py b/plugins/modules/software_center_download.py index 9ea4091..886e0d1 100644 --- a/plugins/modules/software_center_download.py +++ b/plugins/modules/software_center_download.py @@ -26,6 +26,14 @@ required: true type: str softwarecenter_search_query: + description: + - "Deprecated. Use 'search_query' instead." + required: false + type: str + deprecated: + alternative: search_query + removed_in: "1.2.0" + search_query: description: - Filename of the SAP software to download. required: false @@ -50,13 +58,18 @@ - How to handle multiple search results. required: false type: str - query_latest: + search_alternatives: description: - Enable search for alternative packages, when filename is not available. required: false type: bool + dry_run: + description: + - Check availability of SAP Software without downloading. + required: false + type: bool author: - - Lab for SAP Solutions + - SAP LinuxLab ''' @@ -103,12 +116,13 @@ def run_module(): suser_id=dict(type='str', required=True), suser_password=dict(type='str', required=True, no_log=True), softwarecenter_search_query=dict(type='str', required=False, default=''), + search_query=dict(type='str', required=False, default=''), download_link=dict(type='str', required=False, default=''), download_filename=dict(type='str', required=False, default=''), dest=dict(type='str', required=True), dry_run=dict(type='bool', required=False, default=False), deduplicate=dict(type='str', required=False, default=''), - query_latest=dict(type='bool', required=False, default=False) + search_alternatives=dict(type='bool', required=False, default=False) ) # Define result dictionary objects to be passed back to Ansible @@ -130,13 +144,21 @@ def run_module(): # Define variables based on module inputs username = module.params.get('suser_id') password = module.params.get('suser_password') - query = module.params.get('softwarecenter_search_query') + + if module.params['search_query'] != '': + query = module.params['search_query'] + elif module.params['softwarecenter_search_query'] != '': + query = module.params['softwarecenter_search_query'] + module.warn("The 'softwarecenter_search_query' is deprecated and will be removed in a future version. Use 'search_query' instead.") + else: + query = None + download_link= module.params.get('download_link') download_filename= module.params.get('download_filename') dest = module.params.get('dest') dry_run = module.params.get('dry_run') deduplicate = module.params.get('deduplicate') - latest = module.params.get('query_latest') + search_alternatives = module.params.get('search_alternatives') # Main run @@ -154,29 +176,35 @@ def run_module(): sap_sso_login(username, password) # Execute search_software_filename first to get download link and filename - download_latest = False # True if query_latest was successful + alternative_found = False # True if search_alternatives was successful if query: - download_link, download_filename, download_latest = search_software_filename(query,deduplicate,latest) - # Recheck file availability if query_latest is used - if latest: + download_link, download_filename, alternative_found = search_software_filename(query,deduplicate,search_alternatives) + # Recheck file availability if search_alternatives is used + if search_alternatives: pattern = dest + '/**/' + os.path.splitext(download_filename)[0] + '*' for file in glob.glob(pattern, recursive=True): if os.path.exists(file): module.exit_json(skipped=True, msg=f"Alternative file '{os.path.basename(file)}' already exists - original file '{query}' is not available to download") - # execute download_software + # Ensure that download_link is provided when query was not provided + if not download_link: + module.fail_json(msg="Missing parameters 'query' or 'download_link'.") + + # Exit module before download when dry_run is true if dry_run: available = is_download_link_available(download_link) - if available: - module.exit_json(changed=False, msg="download link {} is available".format(download_link)) + if available and query and not alternative_found: + module.exit_json(changed=False, msg="SAP Software is available: {}".format(download_filename)) + elif available and query and alternative_found: + module.exit_json(changed=False, msg="Alternative SAP Software is available: {} - original file {} is not available".format(download_filename, query)) else: - module.fail_json(msg="download link {} is not available".format(download_link)) + module.fail_json(msg="Download link {} is not available".format(download_link)) download_software(download_link, download_filename, dest) # Process return dictionary for Ansible result['changed'] = True - if query and download_latest: + if query and alternative_found: result['msg'] = f"Successfully downloaded alternative SAP software: {download_filename} - original file '{query}' is not available to download" else: result['msg'] = f"Successfully downloaded SAP software: {download_filename}" From 71b2bd8390aa394b1ffd058f74f9ac3b7f8c2779 Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Tue, 25 Mar 2025 16:02:45 +0100 Subject: [PATCH 04/26] feat: enhance search logic for existing files --- ...unchpad_software_center_download_runner.py | 2 +- plugins/modules/software_center_download.py | 53 ++++++++++++++----- 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/plugins/module_utils/sap_launchpad_software_center_download_runner.py b/plugins/module_utils/sap_launchpad_software_center_download_runner.py index 241ab71..d4914fd 100644 --- a/plugins/module_utils/sap_launchpad_software_center_download_runner.py +++ b/plugins/module_utils/sap_launchpad_software_center_download_runner.py @@ -72,7 +72,7 @@ def search_software_filename(name, deduplicate, search_alternatives): software_found = software_search_alternatives_filtered[0] alternative_found = True else: - raise ValueError(f'File {name} is not available to download') + raise ValueError(f'File {name} is not available to download. Enable "search_alternatives" to search for alternatives.') elif files_count > 1 and deduplicate == '': names = [s['Title'] for s in software_filtered] diff --git a/plugins/modules/software_center_download.py b/plugins/modules/software_center_download.py index 886e0d1..a4a2e95 100644 --- a/plugins/modules/software_center_download.py +++ b/plugins/modules/software_center_download.py @@ -165,13 +165,28 @@ def run_module(): try: - # Search directory and subdirectories for filename without file extension - filename = query if query else download_filename - pattern = dest + '/**/' + os.path.splitext(filename)[0] + '*' - for file in glob.glob(pattern, recursive=True): - if os.path.exists(file): - module.exit_json(skipped=True, msg=f"File '{os.path.basename(file)}' already exists") + # Ensure that required parameters are provided + if not (query or (download_link and download_filename)): + module.fail_json(msg="Either 'search_query' or both 'download_link' and 'download_filename' must be provided.") + # Search for existing files using exact filename + filename = query if query else download_filename + filename_exact = os.path.join(dest, filename) + if os.path.exists(filename_exact): + module.exit_json(skipped=True, msg=f"File '{filename}' already exists") + else: + # Exact file not found, search with pattern + # pattern = dest + '/**/' + os.path.splitext(filename)[0] + '*' # old pattern + filename_base = os.path.splitext(filename)[0] + filename_ext = os.path.splitext(filename)[1] + filename_pattern = os.path.join(dest, "**", filename_base + "*" + filename_ext) + filename_similar = glob.glob(filename_pattern, recursive=True) + + # Skip if similar files were found and search_alternatives was not set. + if filename_similar and not (query and search_alternatives): + filename_similar_names = [os.path.basename(f) for f in filename_similar] + module.exit_json(skipped=True, msg=f"Similar file(s) already exist: {', '.join(filename_similar_names)}") + # Initiate login with given credentials sap_sso_login(username, password) @@ -179,12 +194,22 @@ def run_module(): alternative_found = False # True if search_alternatives was successful if query: download_link, download_filename, alternative_found = search_software_filename(query,deduplicate,search_alternatives) - # Recheck file availability if search_alternatives is used - if search_alternatives: - pattern = dest + '/**/' + os.path.splitext(download_filename)[0] + '*' - for file in glob.glob(pattern, recursive=True): - if os.path.exists(file): - module.exit_json(skipped=True, msg=f"Alternative file '{os.path.basename(file)}' already exists - original file '{query}' is not available to download") + + # Search for existing files again with alternative filename + if search_alternatives and alternative_found: + filename_alternative_exact = os.path.join(dest, download_filename) + if os.path.exists(filename_alternative_exact): + module.exit_json(skipped=True, msg=f"Alternative file '{download_filename}' already exists - original file '{query}' is not available to download") + else: + filename_alternative_base = os.path.splitext(download_filename)[0] + filename_alternative_ext = os.path.splitext(download_filename)[1] + filename_alternative_pattern = os.path.join(dest, "**", filename_alternative_base + "*" + filename_alternative_ext) + filename_alternative_similar = glob.glob(filename_alternative_pattern, recursive=True) + + # Skip if similar files were found and search_alternatives was not set. + if filename_alternative_similar: + filename_alternative_similar_names = [os.path.basename(f) for f in filename_alternative_similar] + module.exit_json(skipped=True, msg=f"Similar alternative file(s) already exist: {', '.join(filename_alternative_similar_names)}") # Ensure that download_link is provided when query was not provided if not download_link: @@ -194,9 +219,9 @@ def run_module(): if dry_run: available = is_download_link_available(download_link) if available and query and not alternative_found: - module.exit_json(changed=False, msg="SAP Software is available: {}".format(download_filename)) + module.exit_json(changed=False, msg="SAP Software is available to download: {}".format(download_filename)) elif available and query and alternative_found: - module.exit_json(changed=False, msg="Alternative SAP Software is available: {} - original file {} is not available".format(download_filename, query)) + module.exit_json(changed=False, msg="Alternative SAP Software is available to download: {} - original file {} is not available".format(download_filename, query)) else: module.fail_json(msg="Download link {} is not available".format(download_link)) From 181dcb3ba1d4f853f01b49b685edcf75c868cd86 Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Wed, 26 Mar 2025 15:34:11 +0100 Subject: [PATCH 05/26] new return parameters for role consumption and improved result output --- plugins/modules/software_center_download.py | 68 +++++++++++++++------ 1 file changed, 48 insertions(+), 20 deletions(-) diff --git a/plugins/modules/software_center_download.py b/plugins/modules/software_center_download.py index a4a2e95..194dd3f 100644 --- a/plugins/modules/software_center_download.py +++ b/plugins/modules/software_center_download.py @@ -95,6 +95,14 @@ description: the status of the process returned: always type: str +filename: + description: the name of the original or alternative file found to download. + returned: always + type: str +alternative: + description: true if alternative file was found + returned: always + type: bool ''' @@ -125,22 +133,12 @@ def run_module(): search_alternatives=dict(type='bool', required=False, default=False) ) - # Define result dictionary objects to be passed back to Ansible - result = dict( - changed=False, - msg='' - ) - # Instantiate module module = AnsibleModule( argument_spec=module_args, supports_check_mode=True ) - # Check mode - if module.check_mode: - module.exit_json(**result) - # Define variables based on module inputs username = module.params.get('suser_id') password = module.params.get('suser_password') @@ -160,20 +158,39 @@ def run_module(): deduplicate = module.params.get('deduplicate') search_alternatives = module.params.get('search_alternatives') + # Define result dictionary objects to be passed back to Ansible + result = dict( + changed=False, + msg='', + filename=download_filename, + alternative=False, + skipped=False, + failed=False + ) + + # Check mode + if module.check_mode: + module.exit_json(**result) - # Main run + # Main try: # Ensure that required parameters are provided if not (query or (download_link and download_filename)): - module.fail_json(msg="Either 'search_query' or both 'download_link' and 'download_filename' must be provided.") + module.fail_json( + msg="Either 'search_query' or both 'download_link' and 'download_filename' must be provided." + ) # Search for existing files using exact filename filename = query if query else download_filename filename_exact = os.path.join(dest, filename) + result['filename'] = filename + if os.path.exists(filename_exact): - module.exit_json(skipped=True, msg=f"File '{filename}' already exists") + result['skipped'] = True + result['msg'] = f"File already exists: {filename}" + module.exit_json(**result) else: # Exact file not found, search with pattern # pattern = dest + '/**/' + os.path.splitext(filename)[0] + '*' # old pattern @@ -185,7 +202,9 @@ def run_module(): # Skip if similar files were found and search_alternatives was not set. if filename_similar and not (query and search_alternatives): filename_similar_names = [os.path.basename(f) for f in filename_similar] - module.exit_json(skipped=True, msg=f"Similar file(s) already exist: {', '.join(filename_similar_names)}") + result['skipped'] = True + result['msg'] = f"Similar file(s) already exist: {', '.join(filename_similar_names)}" + module.exit_json(**result) # Initiate login with given credentials sap_sso_login(username, password) @@ -197,9 +216,14 @@ def run_module(): # Search for existing files again with alternative filename if search_alternatives and alternative_found: + result['filename'] = download_filename + result['alternative'] = True + filename_alternative_exact = os.path.join(dest, download_filename) if os.path.exists(filename_alternative_exact): - module.exit_json(skipped=True, msg=f"Alternative file '{download_filename}' already exists - original file '{query}' is not available to download") + result['skipped'] = True + result['msg'] = f"Alternative file already exists: {download_filename} - original file {query} is not available to download" + module.exit_json(**result) else: filename_alternative_base = os.path.splitext(download_filename)[0] filename_alternative_ext = os.path.splitext(download_filename)[1] @@ -209,7 +233,9 @@ def run_module(): # Skip if similar files were found and search_alternatives was not set. if filename_alternative_similar: filename_alternative_similar_names = [os.path.basename(f) for f in filename_alternative_similar] - module.exit_json(skipped=True, msg=f"Similar alternative file(s) already exist: {', '.join(filename_alternative_similar_names)}") + result['skipped'] = True + result['msg'] = f"Similar alternative file(s) already exist: {', '.join(filename_alternative_similar_names)}" + module.exit_json(**result) # Ensure that download_link is provided when query was not provided if not download_link: @@ -219,18 +245,20 @@ def run_module(): if dry_run: available = is_download_link_available(download_link) if available and query and not alternative_found: - module.exit_json(changed=False, msg="SAP Software is available to download: {}".format(download_filename)) + result['msg'] = f"SAP Software is available to download: {download_filename}" + module.exit_json(**result) elif available and query and alternative_found: - module.exit_json(changed=False, msg="Alternative SAP Software is available to download: {} - original file {} is not available".format(download_filename, query)) + result['msg'] = f"Alternative SAP Software is available to download: {download_filename} - original file {query} is not available to download" + module.exit_json(**result) else: module.fail_json(msg="Download link {} is not available".format(download_link)) download_software(download_link, download_filename, dest) - # Process return dictionary for Ansible + # Update final results json result['changed'] = True if query and alternative_found: - result['msg'] = f"Successfully downloaded alternative SAP software: {download_filename} - original file '{query}' is not available to download" + result['msg'] = f"Successfully downloaded alternative SAP software: {download_filename} - original file {query} is not available to download" else: result['msg'] = f"Successfully downloaded SAP software: {download_filename}" From ff4c33206a3af21f561b6c5fb26a673ac49effc6 Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Wed, 26 Mar 2025 17:06:25 +0100 Subject: [PATCH 06/26] rename dest argument to download_path --- plugins/modules/software_center_download.py | 26 ++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/plugins/modules/software_center_download.py b/plugins/modules/software_center_download.py index 194dd3f..f6d9002 100644 --- a/plugins/modules/software_center_download.py +++ b/plugins/modules/software_center_download.py @@ -48,9 +48,9 @@ - Download filename of the SAP software. required: false type: str - dest: + download_path: description: - - Destination folder. + - Destination folder path. required: true type: str deduplicate: @@ -78,16 +78,16 @@ community.sap_launchpad.sap_launchpad_software_center_download: suser_id: 'SXXXXXXXX' suser_password: 'password' - softwarecenter_search_query: + search_query: - 'SAPCAR_1324-80000936.EXE' - dest: "/tmp/" + download_path: "/tmp/" - name: Download using direct link and filename community.sap_launchpad.software_center_download: suser_id: 'SXXXXXXXX' suser_password: 'password' download_link: 'https://softwaredownloads.sap.com/file/0010000000048502015' download_filename: 'IW_FNDGC100.SAR' - dest: "/tmp/" + download_path: "/tmp/" ''' RETURN = r''' @@ -127,7 +127,7 @@ def run_module(): search_query=dict(type='str', required=False, default=''), download_link=dict(type='str', required=False, default=''), download_filename=dict(type='str', required=False, default=''), - dest=dict(type='str', required=True), + download_path=dict(type='str', required=True), dry_run=dict(type='bool', required=False, default=False), deduplicate=dict(type='str', required=False, default=''), search_alternatives=dict(type='bool', required=False, default=False) @@ -151,9 +151,9 @@ def run_module(): else: query = None + download_path = module.params['download_path'] download_link= module.params.get('download_link') download_filename= module.params.get('download_filename') - dest = module.params.get('dest') dry_run = module.params.get('dry_run') deduplicate = module.params.get('deduplicate') search_alternatives = module.params.get('search_alternatives') @@ -184,7 +184,7 @@ def run_module(): # Search for existing files using exact filename filename = query if query else download_filename - filename_exact = os.path.join(dest, filename) + filename_exact = os.path.join(download_path, filename) result['filename'] = filename if os.path.exists(filename_exact): @@ -193,10 +193,10 @@ def run_module(): module.exit_json(**result) else: # Exact file not found, search with pattern - # pattern = dest + '/**/' + os.path.splitext(filename)[0] + '*' # old pattern + # pattern = download_path + '/**/' + os.path.splitext(filename)[0] + '*' # old pattern filename_base = os.path.splitext(filename)[0] filename_ext = os.path.splitext(filename)[1] - filename_pattern = os.path.join(dest, "**", filename_base + "*" + filename_ext) + filename_pattern = os.path.join(download_path, "**", filename_base + "*" + filename_ext) filename_similar = glob.glob(filename_pattern, recursive=True) # Skip if similar files were found and search_alternatives was not set. @@ -219,7 +219,7 @@ def run_module(): result['filename'] = download_filename result['alternative'] = True - filename_alternative_exact = os.path.join(dest, download_filename) + filename_alternative_exact = os.path.join(download_path, download_filename) if os.path.exists(filename_alternative_exact): result['skipped'] = True result['msg'] = f"Alternative file already exists: {download_filename} - original file {query} is not available to download" @@ -227,7 +227,7 @@ def run_module(): else: filename_alternative_base = os.path.splitext(download_filename)[0] filename_alternative_ext = os.path.splitext(download_filename)[1] - filename_alternative_pattern = os.path.join(dest, "**", filename_alternative_base + "*" + filename_alternative_ext) + filename_alternative_pattern = os.path.join(download_path, "**", filename_alternative_base + "*" + filename_alternative_ext) filename_alternative_similar = glob.glob(filename_alternative_pattern, recursive=True) # Skip if similar files were found and search_alternatives was not set. @@ -253,7 +253,7 @@ def run_module(): else: module.fail_json(msg="Download link {} is not available".format(download_link)) - download_software(download_link, download_filename, dest) + download_software(download_link, download_filename, download_path) # Update final results json result['changed'] = True From a259405b5b2843de5d7a2fb768017e7d46c3d972 Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Thu, 27 Mar 2025 11:26:39 +0100 Subject: [PATCH 07/26] fix for CD media with numerical names not skipping existing --- plugins/modules/software_center_download.py | 74 ++++++++++++++------- 1 file changed, 51 insertions(+), 23 deletions(-) diff --git a/plugins/modules/software_center_download.py b/plugins/modules/software_center_download.py index f6d9002..4e1fc79 100644 --- a/plugins/modules/software_center_download.py +++ b/plugins/modules/software_center_download.py @@ -116,6 +116,29 @@ from ..module_utils.sap_launchpad_software_center_download_runner import * from ..module_utils.sap_id_sso import sap_sso_login +def _check_similar_files(download_path, filename): + """ + Checks for similar files in the download path based on the given filename. + + Args: + download_path (str): The path where files are downloaded. + filename (str): The filename to check for similar files. + + Returns: + bool: True if similar files exist, False otherwise. + filename_similar_names: A list of similar filenames if they exist, empty list otherwise. + """ + # pattern = download_path + '/**/' + os.path.splitext(filename)[0] + filename_base = os.path.splitext(filename)[0] + filename_pattern = os.path.join(download_path, "**", filename_base) + filename_similar = glob.glob(filename_pattern, recursive=True) + + if filename_similar: + filename_similar_names = [os.path.basename(f) for f in filename_similar] + return True, filename_similar_names + else: + return False, [] + def run_module(): @@ -192,16 +215,9 @@ def run_module(): result['msg'] = f"File already exists: {filename}" module.exit_json(**result) else: - # Exact file not found, search with pattern - # pattern = download_path + '/**/' + os.path.splitext(filename)[0] + '*' # old pattern - filename_base = os.path.splitext(filename)[0] - filename_ext = os.path.splitext(filename)[1] - filename_pattern = os.path.join(download_path, "**", filename_base + "*" + filename_ext) - filename_similar = glob.glob(filename_pattern, recursive=True) - - # Skip if similar files were found and search_alternatives was not set. - if filename_similar and not (query and search_alternatives): - filename_similar_names = [os.path.basename(f) for f in filename_similar] + # Exact file not found, search for similar files with pattern + filename_similar_exists, filename_similar_names = _check_similar_files(download_path, filename) + if filename_similar_exists and not (query and search_alternatives): result['skipped'] = True result['msg'] = f"Similar file(s) already exist: {', '.join(filename_similar_names)}" module.exit_json(**result) @@ -213,9 +229,9 @@ def run_module(): alternative_found = False # True if search_alternatives was successful if query: download_link, download_filename, alternative_found = search_software_filename(query,deduplicate,search_alternatives) - + # Search for existing files again with alternative filename - if search_alternatives and alternative_found: + if search_alternatives and alternative_found: result['filename'] = download_filename result['alternative'] = True @@ -225,17 +241,29 @@ def run_module(): result['msg'] = f"Alternative file already exists: {download_filename} - original file {query} is not available to download" module.exit_json(**result) else: - filename_alternative_base = os.path.splitext(download_filename)[0] - filename_alternative_ext = os.path.splitext(download_filename)[1] - filename_alternative_pattern = os.path.join(download_path, "**", filename_alternative_base + "*" + filename_alternative_ext) - filename_alternative_similar = glob.glob(filename_alternative_pattern, recursive=True) - - # Skip if similar files were found and search_alternatives was not set. - if filename_alternative_similar: - filename_alternative_similar_names = [os.path.basename(f) for f in filename_alternative_similar] - result['skipped'] = True - result['msg'] = f"Similar alternative file(s) already exist: {', '.join(filename_alternative_similar_names)}" - module.exit_json(**result) + # Exact file not found, search for similar files with pattern + filename_similar_exists, filename_similar_names = _check_similar_files(download_path, download_filename) + if filename_similar_exists: + result['skipped'] = True + result['msg'] = f"Similar alternative file(s) already exist: {', '.join(filename_similar_names)}" + module.exit_json(**result) + + # Triggers for CD Media, where number was changed to name using _get_valid_filename + elif filename != download_filename and not alternative_found: + result['filename'] = download_filename + + if os.path.exists(os.path.join(download_path, download_filename)): + result['skipped'] = True + result['msg'] = f"File already exists with correct name: {download_filename}" + module.exit_json(**result) + else: + # Exact file not found, search for similar files with pattern + filename_similar_exists, filename_similar_names = _check_similar_files(download_path, download_filename) + if filename_similar_exists: + result['skipped'] = True + result['msg'] = f"Similar file(s) already exist for correct name {download_filename}: {', '.join(filename_similar_names)}" + module.exit_json(**result) + # Ensure that download_link is provided when query was not provided if not download_link: From 045395b28cac7da1ad78fec77bd5abfe34b6dff5 Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Thu, 27 Mar 2025 13:14:10 +0100 Subject: [PATCH 08/26] feat: display_id handling and rename dest arg --- ...ap_launchpad_maintenance_planner_runner.py | 20 +++++++++++++++++++ plugins/modules/maintenance_planner_files.py | 6 +++--- .../maintenance_planner_stack_xml_download.py | 18 ++++++++--------- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/plugins/module_utils/sap_launchpad_maintenance_planner_runner.py b/plugins/module_utils/sap_launchpad_maintenance_planner_runner.py index bae7495..81a9679 100644 --- a/plugins/module_utils/sap_launchpad_maintenance_planner_runner.py +++ b/plugins/module_utils/sap_launchpad_maintenance_planner_runner.py @@ -281,6 +281,26 @@ def _get_transaction_name(trans_id): return transaction['trans_name'] +def get_transaction_id(name): + """ + Search transaction ID using transaction Name or Display ID. + + Args: + name: transaction name or display id. + """ + transactions = get_transactions() + transaction_name = [t for t in transactions if t['trans_name'] == name] + if not transaction_name: + # Repeat search using Display ID + transaction_display_id = [t for t in transactions if t['trans_display_id'] == name] + if not transaction_display_id: + raise KeyError(f'Name or Display ID {name} not found in transactionsX') + else: + return transaction_display_id[0]['trans_id'] + else: + return transaction_name[0]['trans_id'] + + def _get_transaction(key, value): transactions = get_transactions() trans = [t for t in transactions if t[key] == value] diff --git a/plugins/modules/maintenance_planner_files.py b/plugins/modules/maintenance_planner_files.py index b52f22f..d38648b 100644 --- a/plugins/modules/maintenance_planner_files.py +++ b/plugins/modules/maintenance_planner_files.py @@ -27,11 +27,11 @@ type: str transaction_name: description: - - Transaction name of your Maintenance Planner session. + - Name or Display ID of your Maintenance Planner transaction. required: true type: str author: - - Lab for SAP Solutions + - SAP LinuxLab ''' @@ -115,7 +115,7 @@ def run_module(): auth_userapps() # EXEC: Get MP stack transaction id from transaction name - transaction_id = get_transaction_id_by_name(transaction_name) + transaction_id = get_transaction_id(transaction_name) # EXEC: Get a json list of download_links and download_filenames download_basket_details = get_transaction_filename_url(transaction_id) diff --git a/plugins/modules/maintenance_planner_stack_xml_download.py b/plugins/modules/maintenance_planner_stack_xml_download.py index a08387b..cf3eabd 100644 --- a/plugins/modules/maintenance_planner_stack_xml_download.py +++ b/plugins/modules/maintenance_planner_stack_xml_download.py @@ -27,16 +27,16 @@ type: str transaction_name: description: - - Transaction name of your Maintenance Planner session. + - Name or Display ID of your Maintenance Planner transaction. required: true type: str - dest: + download_path: description: - - Destination folder. + - Destination folder path. required: true type: str author: - - Lab for SAP Solutions + - SAP LinuxLab ''' @@ -46,7 +46,7 @@ suser_id: 'SXXXXXXXX' suser_password: 'password' transaction_name: 'MP_NEW_INST_20211015_044854' - dest: "/tmp/" + download_path: "/tmp/" register: sap_mp_register - name: Display the list of download links and filenames debug: @@ -79,7 +79,7 @@ def run_module(): suser_id=dict(type='str', required=True), suser_password=dict(type='str', required=True, no_log=True), transaction_name=dict(type='str', required=True), - dest=dict(type='str', required=True) + download_path=dict(type='str', required=True) ) # Define result dictionary objects to be passed back to Ansible @@ -102,7 +102,7 @@ def run_module(): username = module.params.get('suser_id') password = module.params.get('suser_password') transaction_name = module.params.get('transaction_name') - dest = module.params.get('dest') + download_path = module.params.get('download_path') # Main run @@ -115,10 +115,10 @@ def run_module(): auth_userapps() # EXEC: Get MP stack transaction id from transaction name - transaction_id = get_transaction_id_by_name(transaction_name) + transaction_id = get_transaction_id(transaction_name) # EXEC: Download the MP Stack XML file - get_transaction_stack_xml(transaction_id, dest) + get_transaction_stack_xml(transaction_id, download_path) # Process return dictionary for Ansible result['changed'] = True From 7578647c81a44429f1635c26917fe3d204ab5d1f Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Thu, 27 Mar 2025 13:43:40 +0100 Subject: [PATCH 09/26] updated description of transaction_name argument --- plugins/modules/maintenance_planner_files.py | 2 +- plugins/modules/maintenance_planner_stack_xml_download.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/modules/maintenance_planner_files.py b/plugins/modules/maintenance_planner_files.py index d38648b..f15d477 100644 --- a/plugins/modules/maintenance_planner_files.py +++ b/plugins/modules/maintenance_planner_files.py @@ -27,7 +27,7 @@ type: str transaction_name: description: - - Name or Display ID of your Maintenance Planner transaction. + - Transaction Name or Transaction Display ID from Maintenance Planner. required: true type: str author: diff --git a/plugins/modules/maintenance_planner_stack_xml_download.py b/plugins/modules/maintenance_planner_stack_xml_download.py index cf3eabd..3f444db 100644 --- a/plugins/modules/maintenance_planner_stack_xml_download.py +++ b/plugins/modules/maintenance_planner_stack_xml_download.py @@ -27,7 +27,7 @@ type: str transaction_name: description: - - Name or Display ID of your Maintenance Planner transaction. + - Transaction Name or Transaction Display ID from Maintenance Planner. required: true type: str download_path: From 8572ecd82246277bd5ca6895de3510c05dcb3b50 Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Thu, 27 Mar 2025 17:23:05 +0100 Subject: [PATCH 10/26] feat: new role sap_software_download --- .ansible-lint | 24 ++++ roles/sap_software_download/defaults/main.yml | 58 +++++++++ .../tasks/download_files.yml | 62 ++++++++++ .../tasks/download_plan.yml | 61 ++++++++++ roles/sap_software_download/tasks/main.yml | 42 +++++++ .../01_cleanup_python_environment.yml | 8 ++ .../tasks/pre_steps/01_include_variables.yml | 112 ++++++++++++++++++ .../02_prepare_python_environment.yml | 40 +++++++ .../tasks/pre_steps/03_get_plan_files.yml | 57 +++++++++ .../tasks/pre_steps/04_validate_relations.yml | 88 ++++++++++++++ .../tasks/pre_steps/05_validate_sap_hana.yml | 71 +++++++++++ roles/sap_software_download/vars/RedHat.yml | 13 ++ .../sap_software_download/vars/RedHat_10.yml | 12 ++ roles/sap_software_download/vars/Suse.yml | 25 ++++ roles/sap_software_download/vars/main.yml | 10 ++ 15 files changed, 683 insertions(+) create mode 100644 .ansible-lint create mode 100644 roles/sap_software_download/defaults/main.yml create mode 100644 roles/sap_software_download/tasks/download_files.yml create mode 100644 roles/sap_software_download/tasks/download_plan.yml create mode 100644 roles/sap_software_download/tasks/main.yml create mode 100644 roles/sap_software_download/tasks/post_steps/01_cleanup_python_environment.yml create mode 100644 roles/sap_software_download/tasks/pre_steps/01_include_variables.yml create mode 100644 roles/sap_software_download/tasks/pre_steps/02_prepare_python_environment.yml create mode 100644 roles/sap_software_download/tasks/pre_steps/03_get_plan_files.yml create mode 100644 roles/sap_software_download/tasks/pre_steps/04_validate_relations.yml create mode 100644 roles/sap_software_download/tasks/pre_steps/05_validate_sap_hana.yml create mode 100644 roles/sap_software_download/vars/RedHat.yml create mode 100644 roles/sap_software_download/vars/RedHat_10.yml create mode 100644 roles/sap_software_download/vars/Suse.yml create mode 100644 roles/sap_software_download/vars/main.yml diff --git a/.ansible-lint b/.ansible-lint new file mode 100644 index 0000000..829e107 --- /dev/null +++ b/.ansible-lint @@ -0,0 +1,24 @@ +--- +# Collection wide lint-file +# DO NOT CHANGE +exclude_paths: + - .ansible/ + - .cache/ + - .github/ + #- docs/ + - changelogs/ + - playbooks/ + - tests/ +enable_list: + - yaml +skip_list: + # We don't want to enforce new Ansible versions for Galaxy: + - meta-runtime[unsupported-version] + # We do not want to use checks which are marked as experimental: + - experimental + # We use ignore_errors for all the assert tasks, which should be acceptable: + - ignore-errors + # We want to allow single digit version numbers in a role's meta/main.yml file: + - schema + # Allow templating inside name because it creates more detailed output: + - name[template] diff --git a/roles/sap_software_download/defaults/main.yml b/roles/sap_software_download/defaults/main.yml new file mode 100644 index 0000000..efa3501 --- /dev/null +++ b/roles/sap_software_download/defaults/main.yml @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: Apache-2.0 +--- + +# Set which Python version will be used on managed node. +# This is python executable name, which can differ from python package name. +# The default value is set in var file corresponding with OS version. +# Example: python3.11 (Suse), python3.9 (RedHat) +sap_software_download_python_interpreter: + "{{ __sap_software_download_python_interpreter }}" + +# Set which Python package will be installed on managed node. +# The default value is set in var file corresponding with OS version. +# Example: python311 (Suse), python3.9 (RedHat) +sap_software_download_python_package: + "{{ __sap_software_download_python_package }}" + +# Execute role in Python Virtual Environment (recommended). +# If set to `false`, the role will install Python pip modules directly without. +sap_software_download_use_venv: true + +# Set S-User ID with appropriate authorizations for SAP Software download. +sap_software_download_suser_id: '' + +# Set S-User password for user defined in `sap_software_download_suser_id`. +sap_software_download_suser_password: '' + +# Enter list of SAP Software files to download. +sap_software_download_files: [] + +# Enter Transaction Name or Transaction Display ID from SAP Maintenance planner. +sap_software_download_plan: '' + +# Set to `false` to disable searching for alternatives for unavailable files. +sap_software_download_find_alternatives: true + +# Enable handling of duplicate file results. +# Available values: first , last +# sap_software_download_deduplicate: first + +# Set directory where SAP Software will be downloaded. +sap_software_download_directory: '/software' + +# Set to `false` to disable check for known combinations of related files. +# This functionality is not used with `sap_software_download_plan` +# Example: Verify version of IMDB_LCAPPS against IMDB_SERVER if available. +sap_software_download_validate_relations: true + +# Set to `true` to ignore errors during validation of file relations. +# Applicable to `sap_software_download_validate_relations: true` +sap_software_download_ignore_relation_warning: false + +# Set to `true` to ignore role errors when file is not found. +# Download of files will proceed only for files that were found. +sap_software_download_ignore_file_not_found: false + +# Set to `true` to ignore role errors when Maintenance Plan is not found. +# Role will continue with rest of files if `sap_software_download_files` is defined. +sap_software_download_ignore_plan_not_found: false diff --git a/roles/sap_software_download/tasks/download_files.yml b/roles/sap_software_download/tasks/download_files.yml new file mode 100644 index 0000000..7d6f936 --- /dev/null +++ b/roles/sap_software_download/tasks/download_files.yml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: Apache-2.0 +--- + +- name: Download - SAP Software Files - Get files with Python venv + when: sap_software_download_use_venv | d(true) + community.sap_launchpad.software_center_download: + suser_id: "{{ sap_software_download_suser_id }}" + suser_password: "{{ sap_software_download_suser_password }}" + search_query: "{{ item }}" + download_path: "{{ sap_software_download_directory }}" + search_alternatives: "{{ sap_software_download_find_alternatives | d(true) }}" + loop: "{{ sap_software_download_files }}" + loop_control: + label: "{{ item }} : {{ __sap_software_download_files_results_venv.msg | d('') }}" + register: __sap_software_download_files_results_venv + retries: 1 + until: __sap_software_download_files_results_venv is not failed + environment: + PATH: "{{ __sap_software_download_venv.path }}/bin:{{ ansible_env.PATH }}" + PYTHONPATH: "{{ __sap_software_download_venv.path }}/lib/{{ sap_software_download_python_interpreter }}/site-packages" + VIRTUAL_ENV: "{{ __sap_software_download_venv.path }}" + vars: + ansible_python_interpreter: "{{ __sap_software_download_venv.path ~ '/bin/' ~ sap_software_download_python_interpreter }}" + ignore_errors: true # Errors are ignored and validated afterwards + + +- name: Download - SAP Software Files - Get files with default Python + when: not sap_software_download_use_venv | d(true) + community.sap_launchpad.software_center_download: + suser_id: "{{ sap_software_download_suser_id }}" + suser_password: "{{ sap_software_download_suser_password }}" + search_query: "{{ item }}" + download_path: "{{ sap_software_download_directory }}" + search_alternatives: "{{ sap_software_download_find_alternatives | d(true) }}" + loop: "{{ sap_software_download_files }}" + loop_control: + label: "{{ item }} : {{ __sap_software_download_files_results_default.msg | d('') }}" + register: __sap_software_download_files_results_default + retries: 1 + until: __sap_software_download_files_results_default is not failed + vars: + ansible_python_interpreter: "{{ '/usr/bin/' ~ sap_software_download_python_interpreter }}" + ignore_errors: true # Errors are ignored and validated afterwards + + +- name: Download - SAP Software Files - Set fact with software_center_download output + ansible.builtin.set_fact: + __sap_software_download_files_results: "{{ __sap_software_download_files_results_venv + if sap_software_download_use_venv | d(true) else __sap_software_download_files_results_default }}" + +- name: Relation Validation - Show failed results in dry run + ansible.builtin.fail: + msg: | + Download failed for following files: {{ __failed_items | map(attribute='item') | list | join(', ') }} + Either set `sap_software_download_find_alternatives` to `true` to search for alternative files + or ignore this error with `sap_software_download_ignore_file_not_found` set to `true`. + vars: + __failed_items: "{{ __sap_software_download_files_results.results + | selectattr('failed', 'defined') | selectattr('failed', 'true') }}" + when: + - not sap_software_download_ignore_file_not_found + - __failed_items | length > 0 diff --git a/roles/sap_software_download/tasks/download_plan.yml b/roles/sap_software_download/tasks/download_plan.yml new file mode 100644 index 0000000..3752694 --- /dev/null +++ b/roles/sap_software_download/tasks/download_plan.yml @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: Apache-2.0 +--- + +- name: Download - Maintenance Plan - Get files with Python venv + when: sap_software_download_use_venv | d(true) + community.sap_launchpad.software_center_download: + suser_id: "{{ sap_software_download_suser_id }}" + suser_password: "{{ sap_software_download_suser_password }}" + download_link: "{{ item.DirectLink }}" + download_filename: "{{ item.Filename }}" + download_path: "{{ sap_software_download_directory }}" + loop: "{{ __sap_software_download_plan_results.download_basket }}" + loop_control: + label: "{{ item.Filename }} : {{ __sap_software_download_files_plan_results_venv.msg | d('') }}" + register: __sap_software_download_files_plan_results_venv + retries: 1 + until: __sap_software_download_files_plan_results_venv is not failed + environment: + PATH: "{{ __sap_software_download_venv.path }}/bin:{{ ansible_env.PATH }}" + PYTHONPATH: "{{ __sap_software_download_venv.path }}/lib/{{ sap_software_download_python_interpreter }}/site-packages" + VIRTUAL_ENV: "{{ __sap_software_download_venv.path }}" + vars: + ansible_python_interpreter: "{{ __sap_software_download_venv.path ~ '/bin/' ~ sap_software_download_python_interpreter }}" + ignore_errors: true # Errors are ignored and validated afterwards + + +- name: Download - Maintenance Plan - Get files with default Python + when: not sap_software_download_use_venv | d(true) + community.sap_launchpad.software_center_download: + suser_id: "{{ sap_software_download_suser_id }}" + suser_password: "{{ sap_software_download_suser_password }}" + download_link: "{{ item.DirectLink }}" + download_filename: "{{ item.Filename }}" + download_path: "{{ sap_software_download_directory }}" + loop: "{{ __sap_software_download_plan_results.download_basket }}" + loop_control: + label: "{{ item.Filename }} : {{ __sap_software_download_files_plan_results_default.msg | d('') }}" + register: __sap_software_download_files_plan_results_default + retries: 1 + until: __sap_software_download_files_plan_results_default is not failed + vars: + ansible_python_interpreter: "{{ '/usr/bin/' ~ sap_software_download_python_interpreter }}" + ignore_errors: true # Errors are ignored and validated afterwards + + +- name: Download - Maintenance Plan - Set fact with software_center_download output + ansible.builtin.set_fact: + __sap_software_download_files_plan_results: "{{ __sap_software_download_files_plan_results_venv + if sap_software_download_use_venv | d(true) else __sap_software_download_files_plan_results_default }}" + +- name: Download - Maintenance Plan - Show failed results + ansible.builtin.fail: + msg: | + Maintenance Plan file(s) not found: {{ __failed_items | map(attribute='item') | list | join(', ') }} + Verify your Maintenance Plan on SAP Launchpad before retrying. + vars: + __failed_items: "{{ __sap_software_download_files_plan_results.results + | selectattr('failed', 'defined') | selectattr('failed', 'true') }}" + when: + - not sap_software_download_ignore_file_not_found + - __failed_items | length > 0 diff --git a/roles/sap_software_download/tasks/main.yml b/roles/sap_software_download/tasks/main.yml new file mode 100644 index 0000000..586ffca --- /dev/null +++ b/roles/sap_software_download/tasks/main.yml @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: Apache-2.0 +--- + +- name: SAP Software Download - Pre-Steps - Include role variables + ansible.builtin.include_tasks: + file: pre_steps/01_include_variables.yml + +- name: SAP Software Download - Pre-Steps - Prepare Python environment + ansible.builtin.include_tasks: + file: pre_steps/02_prepare_python_environment.yml + +- name: SAP Software Download - Pre-Steps - Find files for Maintenance Plan + ansible.builtin.include_tasks: + file: pre_steps/03_get_plan_files.yml + when: + - sap_software_download_plan | length > 0 + +# NOTE: We do not validate files generated by Maintenance Plan. +- name: SAP Software Download - Pre-Steps - Check related SAP Software combinations + ansible.builtin.include_tasks: + file: pre_steps/04_validate_relations.yml + when: + - sap_software_download_files | length > 0 + - sap_software_download_validate_relations + + +- name: SAP Software Download - Download - Maintenance Plan {{ sap_software_download_plan | d('') }} + ansible.builtin.include_tasks: + file: download_plan.yml + when: + - sap_software_download_plan | length > 0 + - __sap_software_download_plan_results.download_basket is defined + and __sap_software_download_plan_results.download_basket | length > 0 + +- name: SAP Software Download - Download - Files in sap_software_download_files + ansible.builtin.include_tasks: + file: download_files.yml + + +- name: SAP Software Download - Post-Steps - Cleanup Python environment + ansible.builtin.include_tasks: + file: post_steps/01_cleanup_python_environment.yml diff --git a/roles/sap_software_download/tasks/post_steps/01_cleanup_python_environment.yml b/roles/sap_software_download/tasks/post_steps/01_cleanup_python_environment.yml new file mode 100644 index 0000000..e3d303b --- /dev/null +++ b/roles/sap_software_download/tasks/post_steps/01_cleanup_python_environment.yml @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 +--- + +- name: Post-Steps - Remove temporary Python Virtual Environment + ansible.builtin.file: + path: "{{ __sap_software_download_venv.path }}" + state: absent + when: sap_software_download_use_venv | d(true) diff --git a/roles/sap_software_download/tasks/pre_steps/01_include_variables.yml b/roles/sap_software_download/tasks/pre_steps/01_include_variables.yml new file mode 100644 index 0000000..2be6a76 --- /dev/null +++ b/roles/sap_software_download/tasks/pre_steps/01_include_variables.yml @@ -0,0 +1,112 @@ +# SPDX-License-Identifier: Apache-2.0 +--- + +# Example of files loading order: +# 1. Suse.yml / RedHat.yml - Specific to OS family. +# 2. SLES_15.yml / RedHat_9.yml - Specific to distribution (SLES, SLES_SAP or RedHat) and major release. +# 3. SLES_15.6.yml / RedHat_9.2 - Specific to distribution (SLES, SLES_SAP or RedHat) and minor release. +# 4. SLES_SAP_15.yml - Specific to distribution SLES_SAP and major release. +# 5. SLES_SAP_15.6.yml - Specific to distribution SLES_SAP and minor release. +- name: Pre-Steps - Include OS specific variables + ansible.builtin.include_vars: "{{ __vars_file }}" + loop: "{{ __var_files }}" + vars: + __vars_file: "{{ role_path }}/vars/{{ item }}" + __distribution_major: "{{ ansible_distribution ~ '_' ~ ansible_distribution_major_version }}" + __distribution_minor: "{{ ansible_distribution ~ '_' ~ ansible_distribution_version }}" + # Enables loading of shared vars between SLES and SLES_SAP + __distribution_major_split: "{{ ansible_distribution.split('_')[0] ~ '_' ~ ansible_distribution_major_version }}" + __distribution_minor_split: "{{ ansible_distribution.split('_')[0] ~ '_' ~ ansible_distribution_version }}" + __var_files: >- + {{ + [ + ansible_os_family ~ '.yml', + (ansible_distribution ~ '.yml') if ansible_distribution != ansible_os_family else None, + (__distribution_major_split ~ '.yml') if __distribution_major_split != __distribution_major else None, + (__distribution_minor_split ~ '.yml') if __distribution_minor_split != __distribution_minor else None, + __distribution_major ~ '.yml', + __distribution_minor ~ '.yml' + ] | select('defined') | select('string') | list + }} + when: __vars_file is file + + +- name: Pre-Steps - Assert that SAP Software files were specified + ansible.builtin.assert: + that: + - (sap_software_download_files is defined and sap_software_download_files | length > 0) + or (sap_software_download_plan is defined and sap_software_download_plan | length > 0) + fail_msg: | + Neither `sap_software_download_files` or `sap_software_download_plan` are valid. + Ensure that it at least one of them is valid. + +- name: "Pre-Steps - Assert that download directory was provided" + ansible.builtin.assert: + that: + - sap_software_download_directory is defined + - sap_software_download_directory | length > 0 + fail_msg: | + Empty variable `sap_software_download_directory`. + + +- name: "Pre-Steps - Verify variable: sap_software_download_python_interpreter" + ansible.builtin.assert: + that: + - sap_software_download_python_interpreter is defined + - sap_software_download_python_interpreter | length > 0 + fail_msg: | + Empty variable `sap_software_download_python_interpreter`. + Ensure that it contains correct Python interpreter or revert back to defaults. + +- name: "Pre-Steps - Verify variable: sap_software_download_python_package" + ansible.builtin.assert: + that: + - sap_software_download_python_package is defined + - sap_software_download_python_package | length > 0 + fail_msg: | + Empty variable `sap_software_download_python_package`. + Ensure that it contains correct Python package name or revert back to defaults. + +- name: "Pre-Steps - Verify variable: sap_software_download_use_venv" + ansible.builtin.assert: + that: + - sap_software_download_use_venv is defined + - sap_software_download_use_venv is boolean + fail_msg: | + Variable `sap_software_download_use_venv` is not boolean. + +- name: "Pre-Steps - Verify variable: sap_software_download_suser_id" + ansible.builtin.assert: + that: + - sap_software_download_suser_id is defined + - sap_software_download_suser_id | length > 0 + fail_msg: | + Empty variable `sap_software_download_suser_id`. + Enter valid S-User ID with download authorizations. + +- name: "Pre-Steps - Verify variable: sap_software_download_suser_password" + ansible.builtin.assert: + that: + - sap_software_download_suser_password is defined + - sap_software_download_suser_password | length > 0 + fail_msg: | + Empty variable `sap_software_download_suser_password`. + Enter valid S-User password. + no_log: true + +- name: "Pre-Steps - Verify variable: sap_software_download_find_alternatives" + ansible.builtin.assert: + that: + - sap_software_download_find_alternatives is defined + - sap_software_download_find_alternatives is boolean + fail_msg: | + Variable `sap_software_download_find_alternatives` is not boolean. + +- name: "Pre-Steps - Verify variable: sap_software_download_deduplicate" + ansible.builtin.assert: + that: + - sap_software_download_deduplicate in ['first', 'last'] + fail_msg: | + Enter valid option for `sap_software_download_deduplicate` variable. + Options: first, last + when: sap_software_download_deduplicate is defined diff --git a/roles/sap_software_download/tasks/pre_steps/02_prepare_python_environment.yml b/roles/sap_software_download/tasks/pre_steps/02_prepare_python_environment.yml new file mode 100644 index 0000000..bc06da5 --- /dev/null +++ b/roles/sap_software_download/tasks/pre_steps/02_prepare_python_environment.yml @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: Apache-2.0 +--- + +- name: Pre-Steps - Install Python and Python package manager pip + ansible.builtin.package: + name: + - "{{ sap_software_download_python_package }}" + - "{{ sap_software_download_python_package }}-pip" + state: present + + +- name: Pre-Steps - Create download directory {{ sap_software_download_directory }} + ansible.builtin.file: + path: "{{ sap_software_download_directory }}" + state: directory + mode: '0755' + + +- name: Pre-Steps - Block for Python venv preparation + when: sap_software_download_use_venv | d(true) + block: + - name: Pre-Steps - Create temporary directory for venv + ansible.builtin.tempfile: + state: directory + suffix: __sap_software_download_venv + register: __sap_software_download_venv + + - name: Pre-Steps - Install Python modules to Python venv + ansible.builtin.pip: + name: "{{ __sap_software_download_python_modules }}" + virtualenv: "{{ __sap_software_download_venv.path }}" + virtualenv_command: "{{ sap_software_download_python_interpreter }} -m venv" + + +- name: Pre-Steps - Block for default Python preparation + when: not sap_software_download_use_venv | d(true) + block: + - name: Pre-Steps - Install Python modules in default Python + ansible.builtin.pip: + name: "{{ __sap_software_download_python_modules }}" diff --git a/roles/sap_software_download/tasks/pre_steps/03_get_plan_files.yml b/roles/sap_software_download/tasks/pre_steps/03_get_plan_files.yml new file mode 100644 index 0000000..46707f4 --- /dev/null +++ b/roles/sap_software_download/tasks/pre_steps/03_get_plan_files.yml @@ -0,0 +1,57 @@ +# SPDX-License-Identifier: Apache-2.0 +--- + +- name: Maintenance Plan - Get list of files with Python venv + when: sap_software_download_use_venv | d(true) + community.sap_launchpad.maintenance_planner_files: + suser_id: "{{ sap_software_download_suser_id }}" + suser_password: "{{ sap_software_download_suser_password }}" + transaction_name: "{{ sap_software_download_plan }}" + register: __sap_software_download_plan_results_venv + retries: 1 + environment: + PATH: "{{ __sap_software_download_venv.path }}/bin:{{ ansible_env.PATH }}" + PYTHONPATH: "{{ __sap_software_download_venv.path }}/lib/{{ sap_software_download_python_interpreter }}/site-packages" + VIRTUAL_ENV: "{{ __sap_software_download_venv.path }}" + vars: + ansible_python_interpreter: "{{ __sap_software_download_venv.path ~ '/bin/' ~ sap_software_download_python_interpreter }}" + changed_when: false # Getting list of files does not change anything + ignore_errors: true # Errors are ignored and validated afterwards + +- name: Maintenance Plan - Get list of files with default Python + when: not sap_software_download_use_venv | d(true) + community.sap_launchpad.maintenance_planner_files: + suser_id: "{{ sap_software_download_suser_id }}" + suser_password: "{{ sap_software_download_suser_password }}" + transaction_name: "{{ sap_software_download_plan }}" + register: __sap_software_download_plan_results_default + retries: 1 + vars: + ansible_python_interpreter: "{{ '/usr/bin/' ~ sap_software_download_python_interpreter }}" + changed_when: false # Getting list of files does not change anything + ignore_errors: true # Errors are ignored and validated afterwards + +- name: Maintenance Plan - Set fact with maintenance_planner_files output + ansible.builtin.set_fact: + __sap_software_download_plan_results: "{{ __sap_software_download_plan_results_venv + if sap_software_download_use_venv | d(true) else __sap_software_download_plan_results_default }}" + + +- name: Maintenance Plan - Show failed results + ansible.builtin.fail: + msg: | + Maintenance Plan was not found. + Either ensure correct value in `sap_software_download_plan` + or ignore this error with `sap_software_download_ignore_plan_not_found` set to `true`. + when: + - not sap_software_download_ignore_plan_not_found + - __sap_software_download_plan_results.failed + +- name: Maintenance Plan - Show ignored failed results + ansible.builtin.debug: + msg: | + Maintenance Plan was not found. + Error was ignored with `sap_software_download_ignore_plan_not_found` set to `true`. + when: + - sap_software_download_ignore_plan_not_found + - __sap_software_download_plan_results.failed diff --git a/roles/sap_software_download/tasks/pre_steps/04_validate_relations.yml b/roles/sap_software_download/tasks/pre_steps/04_validate_relations.yml new file mode 100644 index 0000000..75cc482 --- /dev/null +++ b/roles/sap_software_download/tasks/pre_steps/04_validate_relations.yml @@ -0,0 +1,88 @@ +# SPDX-License-Identifier: Apache-2.0 +--- + +- name: Block - Relation Validation - Check availability of files (with alternatives) + when: sap_software_download_find_alternatives + block: + + - name: Relation Validation - Check availability of files with Python venv + when: sap_software_download_use_venv | d(true) + community.sap_launchpad.software_center_download: + suser_id: "{{ sap_software_download_suser_id }}" + suser_password: "{{ sap_software_download_suser_password }}" + search_query: "{{ item }}" + dest: "{{ sap_software_download_directory }}" + search_alternatives: "{{ sap_software_download_find_alternatives | d(true) }}" + dry_run: true + loop: "{{ sap_software_download_files }}" + loop_control: + label: "{{ item }} : {{ __sap_software_download_files_results_dryrun_venv.msg | d('') }}" + register: __sap_software_download_files_results_dryrun_venv + retries: 1 + until: __sap_software_download_files_results_dryrun_venv is not failed + environment: + PATH: "{{ __sap_software_download_venv.path }}/bin:{{ ansible_env.PATH }}" + PYTHONPATH: "{{ __sap_software_download_venv.path }}/lib/{{ sap_software_download_python_interpreter }}/site-packages" + VIRTUAL_ENV: "{{ __sap_software_download_venv.path }}" + vars: + ansible_python_interpreter: "{{ __sap_software_download_venv.path ~ '/bin/' ~ sap_software_download_python_interpreter }}" + ignore_errors: true # Errors are ignored and validated afterwards + + + - name: Relation Validation - Check availability of files with default Python + when: not sap_software_download_use_venv | d(true) + community.sap_launchpad.software_center_download: + suser_id: "{{ sap_software_download_suser_id }}" + suser_password: "{{ sap_software_download_suser_password }}" + search_query: "{{ item }}" + dest: "{{ sap_software_download_directory }}" + search_alternatives: "{{ sap_software_download_find_alternatives | d(true) }}" + dry_run: true + loop: "{{ sap_software_download_files }}" + loop_control: + label: "{{ item }} : {{ __sap_software_download_files_results_dryrun_default.msg | d('') }}" + register: __sap_software_download_files_results_dryrun_default + retries: 1 + until: __sap_software_download_files_results_dryrun_default is not failed + vars: + ansible_python_interpreter: "{{ '/usr/bin/' ~ sap_software_download_python_interpreter }}" + ignore_errors: true # Errors are ignored and validated afterwards + + + - name: Relation Validation - Set fact with software_center_download output + ansible.builtin.set_fact: + __sap_software_download_files_results_dryrun: "{{ __sap_software_download_files_results_dryrun_venv + if sap_software_download_use_venv | d(true) else __sap_software_download_files_results_dryrun_default }}" + + - name: Relation Validation - Show failed results + ansible.builtin.fail: + msg: | + Relation validation failed because following files were not found: {{ __failed_items | map(attribute='item') | list | join(', ') }} + Either ensure correct list of files in `sap_software_download_files` + or ignore this error with `sap_software_download_ignore_file_not_found` set to `true`. + vars: + __failed_items: "{{ __sap_software_download_files_results_dryrun.results | selectattr('failed', 'defined') | selectattr('failed', 'true') }}" + when: + - not sap_software_download_ignore_file_not_found + - __failed_items | length > 0 + + +- name: Relation Validation - Define list of files + ansible.builtin.set_fact: + __sap_software_download_files: "{{ sap_software_download_files if not sap_software_download_find_alternatives + else __sap_software_download_files_results_dryrun.results | selectattr('failed', 'false')| map(attribute='filename') | list | unique | d([])}}" + + +- name: Relation Validation - SAP HANA - Multiple IMDB_SERVER files found + ansible.builtin.debug: + msg: Warning - Multiple SAP HANA Database (IMDB_SERVER) found, which can result in inaccurate relation validation! + when: __sap_software_download_files | select('match', '^IMDB_SERVER.*') | list | length > 1 + + +- name: Relation Validation - Include tasks for SAP HANA relation validation + when: __sap_software_download_files | select('match', '^IMDB_SERVER.*') | list | length > 0 + ansible.builtin.include_tasks: + file: pre_steps/05_validate_sap_hana.yml + loop: "{{ __sap_software_download_files | select('match', '^IMDB_SERVER.*') | list }}" + loop_control: + loop_var: __sap_software_download_sap_hana diff --git a/roles/sap_software_download/tasks/pre_steps/05_validate_sap_hana.yml b/roles/sap_software_download/tasks/pre_steps/05_validate_sap_hana.yml new file mode 100644 index 0000000..689c6d2 --- /dev/null +++ b/roles/sap_software_download/tasks/pre_steps/05_validate_sap_hana.yml @@ -0,0 +1,71 @@ +# SPDX-License-Identifier: Apache-2.0 +--- +# Logic behind relation validation: +# - Known variations for files are validated in dedicated tasks +# - Each SAP HANA version have different handling due to different versioning +# - SAP HANA SPS is used to search for + +- name: Relation Validation - SAP HANA - Set HANA variables + ansible.builtin.set_fact: + # "{{ '1' if __sap_software_download_sap_hana | regex_search('^IMDB_SERVER1') else '2' }}" + __sap_software_download_sap_hana_1_sps: + "{{ __sap_software_download_sap_hana.split('_')[2] | regex_replace('[^0-9]', '') }}" # 122 for IMDB_SERVER100_122_35-10009569.SAR + __sap_software_download_sap_hana_1_sps_rev: + "{{ __sap_software_download_sap_hana.split('_')[3].split('-')[0] | regex_replace('[^0-9]', '') }}" # 35 for IMDB_SERVER100_122_35-10009569.SAR + + __sap_software_download_sap_hana_2_version: + "{{ __sap_software_download_sap_hana.split('_')[2] }}" # 084 for IMDB_SERVER20_084_0-80002031.SAR + + +# Warning is triggered only when following conditions are fulfilled +# - At least one IMDB_LCAPPS_1 was found +# - No IMDB_LCAPPS__ was found +# Examples: +# - IMDB_SERVER100_122_35-10009569.SAR - Maintenance Revision 122.35 (SPS12) for HANA DB 1.00 +# - IMDB_LCAPPS_122P_3500-20010426.SAR - LCAPPS for HANA 1.00.122.35 Build 100.47 PL 017 +- name: Relation Check - SAP HANA 1.00 - IMDB_SERVER and IMDB_LCAPPS + ansible.builtin.fail: + msg: | + Warning: Incompatible SAP HANA 1.0 LCAPPS files were found. + Ensure that correct version is selected: SPS {{ __sap_software_download_sap_hana_1_sps + }} and Revision {{ __sap_software_download_sap_hana_1_sps_rev }} + + Expected file pattern: IMDB_LCAPPS_{{ __sap_software_download_sap_hana_1_sps }}*_{{ __sap_software_download_sap_hana_1_sps_rev }}* + Actual files detected: {{ __sap_software_download_sap_hana_1_lcapps_list | join(', ') }} + vars: + __sap_software_download_sap_hana_1_lcapps_list: + "{{ __sap_software_download_files | select('match', '^IMDB_LCAPPS_1.*') | list }}" + __sap_software_download_sap_hana_1_lcapps_list_filtered: + "{{ sap_software_download_files | select('match', '^IMDB_LCAPPS_.*' + ~ __sap_software_download_sap_hana_1_sps ~ '.*_' ~ __sap_software_download_sap_hana_1_sps_rev) | list }}" + ignore_errors: "{{ sap_software_download_ignore_relation_warning | d(false) }}" + when: + - __sap_software_download_sap_hana | regex_search('^IMDB_SERVER1') + - __sap_software_download_sap_hana_1_lcapps_list | length > 0 + - __sap_software_download_sap_hana_1_lcapps_list_filtered | length == 0 + + +# Warning is triggered only when following conditions are fulfilled +# - At least one IMDB_LCAPPS_1 was found +# - No IMDB_LCAPPS__ was found +# Examples: + # - IMDB_SERVER20_084_0-80002031.SAR - Revision 2.00.084.0 (SPS08) for HANA DB 2.0 + # - IMDB_LCAPPS_2084_0-20010426.SAR - LCAPPS for HANA 2.0 Rev 84 Build 101.19 PL 007 +- name: Relation Check - SAP HANA 2.00 - IMDB_SERVER and IMDB_LCAPPS + ansible.builtin.fail: + msg: | + Warning: Incompatible SAP HANA 2.0 LCAPPS files were found. + Ensure that correct version is selected: {{ __sap_software_download_sap_hana_2_version }} + + Expected file pattern: IMDB_LCAPPS_2{{ __sap_software_download_sap_hana_2_version }}* + Actual files detected: {{ __sap_software_download_sap_hana_2_lcapps_list | join(', ') }} + vars: + __sap_software_download_sap_hana_2_lcapps_list: + "{{ __sap_software_download_files | select('match', '^IMDB_LCAPPS_2.*') | list }}" + __sap_software_download_sap_hana_2_lcapps_list_filtered: + "{{ sap_software_download_files | select('match', '^IMDB_LCAPPS_2' ~ __sap_software_download_sap_hana_2_version) | list }}" + ignore_errors: "{{ sap_software_download_ignore_relation_warning | d(false) }}" + when: + - __sap_software_download_sap_hana | regex_search('^IMDB_SERVER2') + - __sap_software_download_sap_hana_2_lcapps_list | length > 0 + - __sap_software_download_sap_hana_2_lcapps_list_filtered | length == 0 diff --git a/roles/sap_software_download/vars/RedHat.yml b/roles/sap_software_download/vars/RedHat.yml new file mode 100644 index 0000000..610fea6 --- /dev/null +++ b/roles/sap_software_download/vars/RedHat.yml @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: Apache-2.0 +--- + +# Variables specific to following versions: +# - Red Hat Linux Enterprise Server 8 +# - Red Hat Linux Enterprise Server 9 + +# Set which Python version will be used on destination node. +# This is python executable name, which can differ from python package name. +__sap_software_download_python_interpreter: 'python3.9' + +# Set which Python package will be installed on destination node. +__sap_software_download_python_package: 'python3.9' diff --git a/roles/sap_software_download/vars/RedHat_10.yml b/roles/sap_software_download/vars/RedHat_10.yml new file mode 100644 index 0000000..122cc07 --- /dev/null +++ b/roles/sap_software_download/vars/RedHat_10.yml @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: Apache-2.0 +--- + +# Variables specific to following versions: +# - Red Hat Linux Enterprise Server 10 + +# Set which Python version will be used on destination node. +# This is python executable name, which can differ from python package name. +__sap_software_download_python_interpreter: 'python3.12' + +# Set which Python package will be installed on destination node. +__sap_software_download_python_package: 'python3.12' diff --git a/roles/sap_software_download/vars/Suse.yml b/roles/sap_software_download/vars/Suse.yml new file mode 100644 index 0000000..2cd68f3 --- /dev/null +++ b/roles/sap_software_download/vars/Suse.yml @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: Apache-2.0 +--- + +# Variables specific to following versions: +# - SUSE Linux Enterprise Server 15 +# - SUSE Linux Enterprise Server 16 + +# NOTE: SLES 15 SP5 introduced Python 3.11. + +# Set which Python version will be used on destination node. +# This is python executable name, which can differ from python package name. +__sap_software_download_python_interpreter: >- + {%- if ansible_distribution_major_version == '15' and ansible_distribution_version.split('.')[1] | int < 5 -%} + python3 + {%- else -%} + python3.11 + {%- endif -%} + +# Set which Python package will be installed on destination node. +__sap_software_download_python_package: >- + {%- if ansible_distribution_major_version == '15' and ansible_distribution_version.split('.')[1] | int < 5 -%} + python3 + {%- else -%} + python311 + {%- endif -%} diff --git a/roles/sap_software_download/vars/main.yml b/roles/sap_software_download/vars/main.yml new file mode 100644 index 0000000..2897c1d --- /dev/null +++ b/roles/sap_software_download/vars/main.yml @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: Apache-2.0 +--- + +# The list of required Python Modules for download module +__sap_software_download_python_modules: + - wheel + - urllib3 + - requests + - beautifulsoup4 + - lxml From fb109c59c484f2153fd99eb3221d8dc82dca7fb1 Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Fri, 28 Mar 2025 16:19:03 +0100 Subject: [PATCH 11/26] feat: hana validation readme, defaults update --- roles/sap_software_download/.yamllint.yml | 27 +++ roles/sap_software_download/README.md | 225 ++++++++++++++++++ roles/sap_software_download/defaults/main.yml | 73 +++--- .../meta/argument_spec.yml | 136 +++++++++++ .../tasks/download_files.yml | 10 +- .../tasks/download_plan.yml | 8 +- .../tasks/pre_steps/01_include_variables.yml | 19 +- .../tasks/pre_steps/04_validate_relations.yml | 14 +- .../tasks/pre_steps/05_validate_sap_hana.yml | 127 ++++++++-- 9 files changed, 572 insertions(+), 67 deletions(-) create mode 100644 roles/sap_software_download/.yamllint.yml create mode 100644 roles/sap_software_download/README.md create mode 100644 roles/sap_software_download/meta/argument_spec.yml diff --git a/roles/sap_software_download/.yamllint.yml b/roles/sap_software_download/.yamllint.yml new file mode 100644 index 0000000..ee4457c --- /dev/null +++ b/roles/sap_software_download/.yamllint.yml @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: Apache-2.0 +--- +# Based on ansible-lint config +extends: default + +rules: + braces: {max-spaces-inside: 1, level: error} + brackets: {max-spaces-inside: 1, level: error} +# colons: {max-spaces-after: -1, level: error} +# commas: {max-spaces-after: -1, level: error} + comments: + require-starting-space: false + min-spaces-from-content: 1 + comments-indentation: disable +# document-start: disable +# empty-lines: {max: 3, level: error} +# hyphens: {level: error} +# indentation: disable +# key-duplicates: enable + line-length: disable +# new-line-at-end-of-file: disable +# new-lines: {type: unix} +# trailing-spaces: disable + truthy: disable + octal-values: + forbid-implicit-octal: true + forbid-explicit-octal: true diff --git a/roles/sap_software_download/README.md b/roles/sap_software_download/README.md new file mode 100644 index 0000000..9b95326 --- /dev/null +++ b/roles/sap_software_download/README.md @@ -0,0 +1,225 @@ + +# sap_software_download Ansible Role + + +## Description + +The Ansible Role `sap_software_download` is used to download SAP Software Media from SAP. + + + + + + +## Prerequisites +The target node must meet the following requirements: + +* **OS Package Repositories:** The operating system must be registered and have access to repositories to install the required Python packages. + * The actual package name is determined by the `sap_software_download_python_package` variable. + * For example, on some systems, these packages might be named `python3` and `python3-pip`. + + + + +## Execution + + + + + + +### Execution Flow + +1. **Input Validation:** The role first checks if all required input variables have been provided. +2. **Python Environment Preparation:** The role prepares the Python environment: + * **Virtual Environment (Default):** A temporary Python virtual environment is created, and all necessary dependencies are installed within it. + * **System Environment:** Alternatively, if `sap_software_download_use_venv` is set to `false`, dependencies are installed directly into the system's default Python environment. +3. **Maintenance Plan File List:** If the `sap_software_download_plan` variable is provided, the role retrieves the list of files associated with the specified Maintenance Plan transaction. +4. **File Relationship Validation:** If `sap_software_download_validate_relations` is `true`, the role performs validation checks on the relationships between the files to be downloaded. + * **Alternative File Search:** If `sap_software_download_find_alternatives` is `true`, the role will search for alternative files if the requested files are not found. + * More information about validation logic is available at [Explanation of relationship validation logic](#explanation-of-relationship-validation-logic) +5. **Maintenance Plan File Download:** If `sap_software_download_plan` is provided, the role downloads the files associated with the Maintenance Plan. +6. **Direct File Download:** If `sap_software_download_files` is provided, the role downloads the specified files. +7. **Virtual Environment Cleanup:** If a temporary Python virtual environment was used, it is removed. + + + +### Example + +Download of SAP Software files using input list +```yaml +--- +- name: Ansible Play for downloading SAP Software + hosts: localhost + become: true + tasks: + - name: Include role sap_software_download + ansible.builtin.include_role: + name: community.sap_launchpad.sap_software_download + vars: + sap_software_download_suser_id: "Enter SAP S-User ID" + sap_software_download_suser_password: "Enter SAP S-User Password" + sap_software_download_directory: "/software" + sap_software_download_files: + - 'SAPCAR_1115-70006178.EXE' + - 'SAPEXE_100-80005509.SAR' +``` + +Download of SAP Software files using Maintenance Plan +```yaml +--- +- name: Ansible Play for downloading SAP Software + hosts: localhost + become: true + tasks: + - name: Include role sap_software_download + ansible.builtin.include_role: + name: community.sap_launchpad.sap_software_download + vars: + sap_software_download_suser_id: "Enter SAP S-User ID" + sap_software_download_suser_password: "Enter SAP S-User Password" + sap_software_download_directory: "/software" + sap_software_download_plan: 'Transaction Name or Display ID from Maintenance Planner' +``` + + + + + + +## Further Information +### Explanation of relationship validation logic +Validation is executed for known combinations of files, where we can validate their name and version.
+Example for SAP HANA Database Server 2.0 with LCAPPS and AFL.
+ +1. All files are examined, and a file starting with `IMDB_SERVER2` is found: `IMDB_SERVER20_084_0-80002031.SAR (Revision 2.00.084.0 (SPS08))`. This indicates a SAP HANA 2.0 database server file. +2. The HANA version and revision are extracted from the file name: `HANA 2.0`, `Revision 084`. +3. Validation for HANA 1.0 is skipped because it expects files starting with `IMDB_SERVER1`. The following steps are only for `IMDB_SERVER2` (HANA 2.0). +4. All files are examined for files starting with `IMDB_LCAPPS_2` (indicating LCAPPS for HANA 2.0). Then the list is filtered to only include files starting with `IMDB_LCAPPS_2084` (indicating LCAPPS for HANA 2.0 revision 084). +5. Validation will have two outcomes: + - A file like `IMDB_LCAPPS_2084_0-20010426.SAR` is present. In this case, validation will pass because the LCAPPS version is compatible with the HANA revision. + - No file starting with `IMDB_LCAPPS_2084` is present, but there are files starting with `IMDB_LCAPPS_2`.
+ This indicates a mismatch because the LCAPPS version is not compatible with the specific HANA revision (084) found in step 2.
+ In this case, validation will fail. This can be prevented by setting `sap_software_download_ignore_relation_warning` to `true`. +6. All files are examined for files starting with `IMDB_AFL20` (indicating AFL for HANA 2.0). Then the list is filtered to only include files starting with `IMDB_AFL20_084` (indicating AFL for HANA 2.0 revision 084). +7. Validation will have two outcomes: + - A file like `IMDB_AFL20_084_1-80001894.SAR` is present. In this case, validation will pass because the AFL version is compatible with the HANA revision. + - No file starting with `IMDB_AFL20_084` is present, but there are files starting with `IMDB_AFL20`.
+ This indicates a mismatch because the AFL version is not compatible with the specific HANA revision (084) found in step 2.
+ In this case, validation will fail. This can be prevented by setting `sap_software_download_ignore_relation_warning` to `true`. + +This validation example checks major and minor release (SPS and Revision), but it does not validate patch version. + + +## License + +Apache 2.0 + + +## Maintainers + +- [Marcel Mamula](https://github.com/marcelmamula) + + +## Role Variables + +### sap_software_download_python_interpreter +- _Type:_ `string`
+ +The Python interpreter executable to use when creating a Python virtual environment.
+**Mandatory** when `sap_software_download_use_venv` is `true`.
+This is the name of the Python executable (e.g., `python3.11`, `python3.9`), which may differ from the Python package name.
+The default value is determined by the operating system and is set in the corresponding OS-specific variables file.
+Examples: `python3.11` (SUSE), `python3.9` (Red Hat)
+ +### sap_software_download_python_package +- _Type:_ `string`
+ +The name of the OS package that provides the desired Python version.
+The Python version provided by this package must match the version specified by `sap_software_download_python_interpreter`.
+The default value is determined by the operating system and is set in the corresponding OS-specific variables file.
+Examples: `python311` (SUSE), `python3.9` (Red Hat)
+ +### sap_software_download_use_venv +- _Type:_ `boolean`
+- _Default:_ `true`
+ +Determines whether to execute the role within a Python virtual environment.
+Using a virtual environment is strongly recommended to isolate dependencies.
+If set to `false`, the role will install Python dependencies directly into the system's Python environment.
+ +### sap_software_download_suser_id +- _Type:_ `string`
+ +The SAP S-User ID with download authorization for SAP software.
+ +### sap_software_download_suser_password +- _Type:_ `string`
+ +The password for the SAP S-User specified in `sap_software_download_suser_id`.
+ +### sap_software_download_files +- _Type:_ `list` with elements of type `string`
+ +A list of SAP software file names to download.
+ +### sap_software_download_plan +- _Type:_ `string`
+ +The name or display ID of a transaction from the SAP Maintenance Planner.
+If provided, the role will download all files associated with this Maintenance Plan transaction.
+ +### sap_software_download_find_alternatives +- _Type:_ `boolean`
+- _Default:_ `true`
+ +Enables searching for alternative files if the requested file is not found.
+Only applies to files specified in `sap_software_download_files`.
+If set to `false`, the role will not search for alternatives.
+ +### sap_software_download_directory +- _Type:_ `string`
+ +The directory where downloaded SAP software files will be stored.
+ +### sap_software_download_validate_relations +- _Type:_ `bool`
+- _Default:_ `true`
+ +Enables validation of relationships between SAP software files.
+Only applies to files specified in `sap_software_download_files`.
+If set to `false`, no relationship validation will be performed.
+Example: Verify version of IMDB_LCAPPS against IMDB_SERVER if IMDB_SERVER was found.
+ +### sap_software_download_ignore_file_not_found +- _Type:_ `bool`
+- _Default:_ `false`
+ +Determines whether to ignore errors when a requested file is not found.
+If set to `true`, the role will continue execution and download other files, even if some files are not found.
+If set to `false`, the role will fail if any requested file is not found.
+ +### sap_software_download_ignore_plan_not_found +- _Type:_ `bool`
+- _Default:_ `false`
+ +Determines whether to ignore errors when a specified Maintenance Plan transaction is not found.
+If set to `true` and a Maintenance Plan is not found, the role will continue execution, downloading any files specified in `sap_software_download_files`.
+If set to `false`, the role will fail if the specified Maintenance Plan is not found.
+ +### sap_software_download_ignore_relation_warning +- _Type:_ `bool`
+- _Default:_ `false`
+ +Determines whether to ignore warnings during file relationship validation.
+If set to `true`, the role will continue execution even if there are warnings during the validation of file relationships.
+If set to `false`, the role will fail if any warnings are encountered during file relationship validation.
+ +### sap_software_download_deduplicate +- _Type:_ `string`
+ +Specifies how to handle duplicate file results when using `sap_software_download_files`.
+If multiple files with the same name are found, this setting determines which one to download.
+- `first`: Download the first file found
+- `last`: Download the last file found.
+ diff --git a/roles/sap_software_download/defaults/main.yml b/roles/sap_software_download/defaults/main.yml index efa3501..d844f9b 100644 --- a/roles/sap_software_download/defaults/main.yml +++ b/roles/sap_software_download/defaults/main.yml @@ -1,58 +1,71 @@ # SPDX-License-Identifier: Apache-2.0 --- -# Set which Python version will be used on managed node. -# This is python executable name, which can differ from python package name. -# The default value is set in var file corresponding with OS version. -# Example: python3.11 (Suse), python3.9 (RedHat) +# The Python interpreter executable to use when creating a Python virtual environment. +# Mandatory when `sap_software_download_use_venv` is `true`. +# This is the name of the Python executable (e.g., `python3.11`, `python3.9`), which may differ from the Python package name. +# The default value is determined by the operating system and is set in the corresponding OS-specific variables file. +# Examples: `python3.11` (SUSE), `python3.9` (Red Hat) sap_software_download_python_interpreter: "{{ __sap_software_download_python_interpreter }}" -# Set which Python package will be installed on managed node. -# The default value is set in var file corresponding with OS version. -# Example: python311 (Suse), python3.9 (RedHat) +# The name of the OS package that provides the desired Python version. +# The Python version provided by this package must match the version specified by `sap_software_download_python_interpreter`. +# The default value is determined by the operating system and is set in the corresponding OS-specific variables file. +# Examples: `python311` (SUSE), `python3.9` (Red Hat) sap_software_download_python_package: "{{ __sap_software_download_python_package }}" -# Execute role in Python Virtual Environment (recommended). -# If set to `false`, the role will install Python pip modules directly without. +# Determines whether to execute the role within a Python virtual environment. +# Using a virtual environment is strongly recommended to isolate dependencies. +# If set to `false`, the role will install Python dependencies directly into the system's Python environment. sap_software_download_use_venv: true -# Set S-User ID with appropriate authorizations for SAP Software download. +# The SAP S-User ID with download authorization for SAP software. sap_software_download_suser_id: '' -# Set S-User password for user defined in `sap_software_download_suser_id`. +# The password for the SAP S-User specified in `sap_software_download_suser_id`. sap_software_download_suser_password: '' -# Enter list of SAP Software files to download. +# A list of SAP software file names to download. sap_software_download_files: [] -# Enter Transaction Name or Transaction Display ID from SAP Maintenance planner. +# The name or display ID of a transaction from the SAP Maintenance Planner. +# If provided, the role will download all files associated with this Maintenance Plan transaction. sap_software_download_plan: '' -# Set to `false` to disable searching for alternatives for unavailable files. +# Enables searching for alternative files if the requested file is not found. +# Only applies to files specified in `sap_software_download_files`. +# If set to `false`, the role will not search for alternatives. sap_software_download_find_alternatives: true -# Enable handling of duplicate file results. -# Available values: first , last -# sap_software_download_deduplicate: first - -# Set directory where SAP Software will be downloaded. +# The directory where downloaded SAP software files will be stored. sap_software_download_directory: '/software' -# Set to `false` to disable check for known combinations of related files. -# This functionality is not used with `sap_software_download_plan` -# Example: Verify version of IMDB_LCAPPS against IMDB_SERVER if available. +# Enables validation of relationships between SAP software files. +# Only applies to files specified in `sap_software_download_files`. +# If set to `false`, no relationship validation will be performed. +# Example: Verify version of IMDB_LCAPPS against IMDB_SERVER if IMDB_SERVER was found. sap_software_download_validate_relations: true -# Set to `true` to ignore errors during validation of file relations. -# Applicable to `sap_software_download_validate_relations: true` -sap_software_download_ignore_relation_warning: false - -# Set to `true` to ignore role errors when file is not found. -# Download of files will proceed only for files that were found. +# Determines whether to ignore errors when a requested file is not found. +# If set to `true`, the role will continue execution and download other files, even if some files are not found. +# If set to `false`, the role will fail if any requested file is not found. sap_software_download_ignore_file_not_found: false -# Set to `true` to ignore role errors when Maintenance Plan is not found. -# Role will continue with rest of files if `sap_software_download_files` is defined. +# Determines whether to ignore errors when a specified Maintenance Plan transaction is not found. +# If set to `true` and a Maintenance Plan is not found, the role will continue execution, +# downloading any files specified in `sap_software_download_files`. +# If set to `false`, the role will fail if the specified Maintenance Plan is not found. sap_software_download_ignore_plan_not_found: false + +# Determines whether to ignore warnings during file relationship validation. +# If set to `true`, the role will continue execution even if there are warnings during the validation of file relationships. +# If set to `false`, the role will fail if any warnings are encountered during file relationship validation. +sap_software_download_ignore_relation_warning: false + +# Specifies how to handle duplicate file results when using `sap_software_download_files`. +# If multiple files with the same name are found, this setting determines which one to download. +# `first`: Download the first file found. +# `last`: Download the last file found. +# sap_software_download_deduplicate: first diff --git a/roles/sap_software_download/meta/argument_spec.yml b/roles/sap_software_download/meta/argument_spec.yml new file mode 100644 index 0000000..19016b9 --- /dev/null +++ b/roles/sap_software_download/meta/argument_spec.yml @@ -0,0 +1,136 @@ +# SPDX-License-Identifier: Apache-2.0 +--- +# Requires: ansible 2.16 +# Argument specifications in this separate file maintain backwards compatibility. +argument_specs: + + main: + short_description: Downloads SAP Software Media from SAP using an S-User ID and password. + description: + - This role downloads SAP Software Media from SAP using an S-User ID and password. + - It supports both direct file downloads and Maintenance Plan transactions. + options: + + sap_software_download_python_interpreter: + description: + - If set to `false`, the role will only execute or verify the installation or configuration steps of SAP notes. + - Default is to perform installation and configuration steps. + required: false + type: str + - The Python interpreter executable to use when creating a Python virtual environment. + - Mandatory when `sap_software_download_use_venv` is `true`. + - This is the name of the Python executable (e.g., `python3.11`, `python3.9`), which may differ from the Python package name. + - The default value is determined by the operating system and is set in the corresponding OS-specific variables file. + - Examples are `python3.11` (SUSE), `python3.9` (Red Hat) + + sap_software_download_python_package: + type: str + required: true + description: + - The name of the OS package that provides the desired Python version. + - The Python version provided by this package must match the version specified by `sap_software_download_python_interpreter`. + - The default value is determined by the operating system and is set in the corresponding OS-specific variables file. + - Examples are `python311` (SUSE), `python3.9` (Red Hat) + + sap_software_download_use_venv: + type: bool + required: true + default: true + description: + - Determines whether to execute the role within a Python virtual environment. + - Using a virtual environment is strongly recommended to isolate dependencies. + - If set to `false`, the role will install Python dependencies directly into the system's Python environment. + + sap_software_download_suser_id: + type: str + required: true + description: + - The SAP S-User ID with download authorization for SAP software. + + sap_software_download_suser_password: + type: str + required: true + no_log: true + description: + - The password for the SAP S-User specified in `sap_software_download_suser_id`. + + sap_software_download_files: + type: list + elements: str + required: false + default: [] + description: + - A list of SAP software file names to download. + + sap_software_download_plan: + type: str + required: false + default: "" + description: + - The name or display ID of a transaction from the SAP Maintenance Planner. + - If provided, the role will download all files associated with this Maintenance Plan transaction. + + sap_software_download_find_alternatives: + type: bool + required: true + default: true + description: + - Enables searching for alternative files if the requested file is not found. + - Only applies to files specified in `sap_software_download_files`. + - If set to `false`, the role will not search for alternatives. + + sap_software_download_directory: + type: str + required: true + default: "/software" + description: + - The directory where downloaded SAP software files will be stored. + + sap_software_download_validate_relations: + type: bool + required: true + default: true + description: + - Enables validation of relationships between SAP software files. + - Only applies to files specified in `sap_software_download_files`. + - If set to `false`, no relationship validation will be performed. + - Example Verify version of IMDB_LCAPPS against IMDB_SERVER if IMDB_SERVER was found. + + sap_software_download_ignore_file_not_found: + type: bool + required: true + default: false + description: + - Determines whether to ignore errors when a requested file is not found. + - If set to `true`, the role will continue execution and download other files, even if some files are not found. + - If set to `false`, the role will fail if any requested file is not found. + + sap_software_download_ignore_plan_not_found: + type: bool + required: true + default: false + description: + - Determines whether to ignore errors when a specified Maintenance Plan transaction is not found. + - If set to `true` and a Maintenance Plan is not found, the role will continue execution, + - downloading any files specified in `sap_software_download_files`. + - If set to `false`, the role will fail if the specified Maintenance Plan is not found. + + sap_software_download_ignore_relation_warning: + type: bool + required: true + default: false + description: + - Determines whether to ignore warnings during file relationship validation. + - If set to `true`, the role will continue execution even if there are warnings during the validation of file relationships. + - If set to `false`, the role will fail if any warnings are encountered during file relationship validation. + + sap_software_download_deduplicate: + type: str + required: false + default: "first" + choices: ["first", "last"] + description: + - Specifies how to handle duplicate file results when using `sap_software_download_files`. + - If multiple files with the same name are found, this setting determines which one to download. + - Value `first` - Download the first file found. + - Value `last` - Download the last file found. diff --git a/roles/sap_software_download/tasks/download_files.yml b/roles/sap_software_download/tasks/download_files.yml index 7d6f936..0e7ba73 100644 --- a/roles/sap_software_download/tasks/download_files.yml +++ b/roles/sap_software_download/tasks/download_files.yml @@ -2,14 +2,15 @@ --- - name: Download - SAP Software Files - Get files with Python venv - when: sap_software_download_use_venv | d(true) community.sap_launchpad.software_center_download: suser_id: "{{ sap_software_download_suser_id }}" suser_password: "{{ sap_software_download_suser_password }}" search_query: "{{ item }}" download_path: "{{ sap_software_download_directory }}" search_alternatives: "{{ sap_software_download_find_alternatives | d(true) }}" - loop: "{{ sap_software_download_files }}" + deduplicate: "{{ sap_software_download_deduplicate | d('') }}" + # Loop condition acts as when conditional + loop: "{{ sap_software_download_files if sap_software_download_use_venv | d(true) else [] }}" loop_control: label: "{{ item }} : {{ __sap_software_download_files_results_venv.msg | d('') }}" register: __sap_software_download_files_results_venv @@ -25,14 +26,15 @@ - name: Download - SAP Software Files - Get files with default Python - when: not sap_software_download_use_venv | d(true) community.sap_launchpad.software_center_download: suser_id: "{{ sap_software_download_suser_id }}" suser_password: "{{ sap_software_download_suser_password }}" search_query: "{{ item }}" download_path: "{{ sap_software_download_directory }}" search_alternatives: "{{ sap_software_download_find_alternatives | d(true) }}" - loop: "{{ sap_software_download_files }}" + deduplicate: "{{ sap_software_download_deduplicate | d('') }}" + # Loop condition acts as when conditional + loop: "{{ sap_software_download_files if not sap_software_download_use_venv | d(true) else [] }}" loop_control: label: "{{ item }} : {{ __sap_software_download_files_results_default.msg | d('') }}" register: __sap_software_download_files_results_default diff --git a/roles/sap_software_download/tasks/download_plan.yml b/roles/sap_software_download/tasks/download_plan.yml index 3752694..0a33c5b 100644 --- a/roles/sap_software_download/tasks/download_plan.yml +++ b/roles/sap_software_download/tasks/download_plan.yml @@ -2,14 +2,14 @@ --- - name: Download - Maintenance Plan - Get files with Python venv - when: sap_software_download_use_venv | d(true) community.sap_launchpad.software_center_download: suser_id: "{{ sap_software_download_suser_id }}" suser_password: "{{ sap_software_download_suser_password }}" download_link: "{{ item.DirectLink }}" download_filename: "{{ item.Filename }}" download_path: "{{ sap_software_download_directory }}" - loop: "{{ __sap_software_download_plan_results.download_basket }}" + # Loop condition acts as when conditional + loop: "{{ __sap_software_download_plan_results.download_basket if sap_software_download_use_venv | d(true) else [] }}" loop_control: label: "{{ item.Filename }} : {{ __sap_software_download_files_plan_results_venv.msg | d('') }}" register: __sap_software_download_files_plan_results_venv @@ -25,14 +25,14 @@ - name: Download - Maintenance Plan - Get files with default Python - when: not sap_software_download_use_venv | d(true) community.sap_launchpad.software_center_download: suser_id: "{{ sap_software_download_suser_id }}" suser_password: "{{ sap_software_download_suser_password }}" download_link: "{{ item.DirectLink }}" download_filename: "{{ item.Filename }}" download_path: "{{ sap_software_download_directory }}" - loop: "{{ __sap_software_download_plan_results.download_basket }}" + # Loop condition acts as when conditional + loop: "{{ __sap_software_download_plan_results.download_basket if not sap_software_download_use_venv | d(true) else [] }}" loop_control: label: "{{ item.Filename }} : {{ __sap_software_download_files_plan_results_default.msg | d('') }}" register: __sap_software_download_files_plan_results_default diff --git a/roles/sap_software_download/tasks/pre_steps/01_include_variables.yml b/roles/sap_software_download/tasks/pre_steps/01_include_variables.yml index 2be6a76..35c90a7 100644 --- a/roles/sap_software_download/tasks/pre_steps/01_include_variables.yml +++ b/roles/sap_software_download/tasks/pre_steps/01_include_variables.yml @@ -49,6 +49,14 @@ Empty variable `sap_software_download_directory`. +- name: "Pre-Steps - Verify variable: sap_software_download_use_venv" + ansible.builtin.assert: + that: + - sap_software_download_use_venv is defined + - sap_software_download_use_venv is boolean + fail_msg: | + Variable `sap_software_download_use_venv` is not boolean. + - name: "Pre-Steps - Verify variable: sap_software_download_python_interpreter" ansible.builtin.assert: that: @@ -56,7 +64,8 @@ - sap_software_download_python_interpreter | length > 0 fail_msg: | Empty variable `sap_software_download_python_interpreter`. - Ensure that it contains correct Python interpreter or revert back to defaults. + Ensure that it contains correct Python interpreter or revert back to defaults. + when: sap_software_download_use_venv - name: "Pre-Steps - Verify variable: sap_software_download_python_package" ansible.builtin.assert: @@ -67,14 +76,6 @@ Empty variable `sap_software_download_python_package`. Ensure that it contains correct Python package name or revert back to defaults. -- name: "Pre-Steps - Verify variable: sap_software_download_use_venv" - ansible.builtin.assert: - that: - - sap_software_download_use_venv is defined - - sap_software_download_use_venv is boolean - fail_msg: | - Variable `sap_software_download_use_venv` is not boolean. - - name: "Pre-Steps - Verify variable: sap_software_download_suser_id" ansible.builtin.assert: that: diff --git a/roles/sap_software_download/tasks/pre_steps/04_validate_relations.yml b/roles/sap_software_download/tasks/pre_steps/04_validate_relations.yml index 75cc482..f18d87d 100644 --- a/roles/sap_software_download/tasks/pre_steps/04_validate_relations.yml +++ b/roles/sap_software_download/tasks/pre_steps/04_validate_relations.yml @@ -6,15 +6,16 @@ block: - name: Relation Validation - Check availability of files with Python venv - when: sap_software_download_use_venv | d(true) community.sap_launchpad.software_center_download: suser_id: "{{ sap_software_download_suser_id }}" suser_password: "{{ sap_software_download_suser_password }}" search_query: "{{ item }}" - dest: "{{ sap_software_download_directory }}" + download_path: "{{ sap_software_download_directory }}" search_alternatives: "{{ sap_software_download_find_alternatives | d(true) }}" + deduplicate: "{{ sap_software_download_deduplicate | d('') }}" dry_run: true - loop: "{{ sap_software_download_files }}" + # Loop condition acts as when conditional + loop: "{{ sap_software_download_files if sap_software_download_use_venv | d(true) else [] }}" loop_control: label: "{{ item }} : {{ __sap_software_download_files_results_dryrun_venv.msg | d('') }}" register: __sap_software_download_files_results_dryrun_venv @@ -30,15 +31,16 @@ - name: Relation Validation - Check availability of files with default Python - when: not sap_software_download_use_venv | d(true) community.sap_launchpad.software_center_download: suser_id: "{{ sap_software_download_suser_id }}" suser_password: "{{ sap_software_download_suser_password }}" search_query: "{{ item }}" - dest: "{{ sap_software_download_directory }}" + download_path: "{{ sap_software_download_directory }}" search_alternatives: "{{ sap_software_download_find_alternatives | d(true) }}" + deduplicate: "{{ sap_software_download_deduplicate | d('') }}" dry_run: true - loop: "{{ sap_software_download_files }}" + # Loop condition acts as when conditional + loop: "{{ sap_software_download_files if not sap_software_download_use_venv | d(true) else [] }}" loop_control: label: "{{ item }} : {{ __sap_software_download_files_results_dryrun_default.msg | d('') }}" register: __sap_software_download_files_results_dryrun_default diff --git a/roles/sap_software_download/tasks/pre_steps/05_validate_sap_hana.yml b/roles/sap_software_download/tasks/pre_steps/05_validate_sap_hana.yml index 689c6d2..7500805 100644 --- a/roles/sap_software_download/tasks/pre_steps/05_validate_sap_hana.yml +++ b/roles/sap_software_download/tasks/pre_steps/05_validate_sap_hana.yml @@ -3,23 +3,22 @@ # Logic behind relation validation: # - Known variations for files are validated in dedicated tasks # - Each SAP HANA version have different handling due to different versioning -# - SAP HANA SPS is used to search for - name: Relation Validation - SAP HANA - Set HANA variables ansible.builtin.set_fact: # "{{ '1' if __sap_software_download_sap_hana | regex_search('^IMDB_SERVER1') else '2' }}" - __sap_software_download_sap_hana_1_sps: + __sap_software_download_sap_hana_1_version: "{{ __sap_software_download_sap_hana.split('_')[2] | regex_replace('[^0-9]', '') }}" # 122 for IMDB_SERVER100_122_35-10009569.SAR - __sap_software_download_sap_hana_1_sps_rev: + __sap_software_download_sap_hana_1_revision: "{{ __sap_software_download_sap_hana.split('_')[3].split('-')[0] | regex_replace('[^0-9]', '') }}" # 35 for IMDB_SERVER100_122_35-10009569.SAR __sap_software_download_sap_hana_2_version: "{{ __sap_software_download_sap_hana.split('_')[2] }}" # 084 for IMDB_SERVER20_084_0-80002031.SAR -# Warning is triggered only when following conditions are fulfilled +# Warning conditions: # - At least one IMDB_LCAPPS_1 was found -# - No IMDB_LCAPPS__ was found +# - No IMDB_LCAPPS__ was found # Examples: # - IMDB_SERVER100_122_35-10009569.SAR - Maintenance Revision 122.35 (SPS12) for HANA DB 1.00 # - IMDB_LCAPPS_122P_3500-20010426.SAR - LCAPPS for HANA 1.00.122.35 Build 100.47 PL 017 @@ -27,17 +26,17 @@ ansible.builtin.fail: msg: | Warning: Incompatible SAP HANA 1.0 LCAPPS files were found. - Ensure that correct version is selected: SPS {{ __sap_software_download_sap_hana_1_sps - }} and Revision {{ __sap_software_download_sap_hana_1_sps_rev }} + Ensure that correct version is selected: {{ __sap_software_download_sap_hana_1_version + }}.{{ __sap_software_download_sap_hana_1_revision }} - Expected file pattern: IMDB_LCAPPS_{{ __sap_software_download_sap_hana_1_sps }}*_{{ __sap_software_download_sap_hana_1_sps_rev }}* - Actual files detected: {{ __sap_software_download_sap_hana_1_lcapps_list | join(', ') }} + Expected file pattern: IMDB_LCAPPS_{{ __sap_software_download_sap_hana_1_version }}*_{{ __sap_software_download_sap_hana_1_revision }}* + Actual files detected: {{ __sap_software_download_sap_hana_1_lcapps_list | unique | join(', ') }} vars: __sap_software_download_sap_hana_1_lcapps_list: "{{ __sap_software_download_files | select('match', '^IMDB_LCAPPS_1.*') | list }}" __sap_software_download_sap_hana_1_lcapps_list_filtered: "{{ sap_software_download_files | select('match', '^IMDB_LCAPPS_.*' - ~ __sap_software_download_sap_hana_1_sps ~ '.*_' ~ __sap_software_download_sap_hana_1_sps_rev) | list }}" + ~ __sap_software_download_sap_hana_1_version ~ '.*_' ~ __sap_software_download_sap_hana_1_revision) | list }}" ignore_errors: "{{ sap_software_download_ignore_relation_warning | d(false) }}" when: - __sap_software_download_sap_hana | regex_search('^IMDB_SERVER1') @@ -45,9 +44,9 @@ - __sap_software_download_sap_hana_1_lcapps_list_filtered | length == 0 -# Warning is triggered only when following conditions are fulfilled -# - At least one IMDB_LCAPPS_1 was found -# - No IMDB_LCAPPS__ was found +# Warning conditions: +# - At least one IMDB_LCAPPS_2 was found +# - No IMDB_LCAPPS_2 was found # Examples: # - IMDB_SERVER20_084_0-80002031.SAR - Revision 2.00.084.0 (SPS08) for HANA DB 2.0 # - IMDB_LCAPPS_2084_0-20010426.SAR - LCAPPS for HANA 2.0 Rev 84 Build 101.19 PL 007 @@ -58,7 +57,7 @@ Ensure that correct version is selected: {{ __sap_software_download_sap_hana_2_version }} Expected file pattern: IMDB_LCAPPS_2{{ __sap_software_download_sap_hana_2_version }}* - Actual files detected: {{ __sap_software_download_sap_hana_2_lcapps_list | join(', ') }} + Actual files detected: {{ __sap_software_download_sap_hana_2_lcapps_list | unique | join(', ') }} vars: __sap_software_download_sap_hana_2_lcapps_list: "{{ __sap_software_download_files | select('match', '^IMDB_LCAPPS_2.*') | list }}" @@ -69,3 +68,103 @@ - __sap_software_download_sap_hana | regex_search('^IMDB_SERVER2') - __sap_software_download_sap_hana_2_lcapps_list | length > 0 - __sap_software_download_sap_hana_2_lcapps_list_filtered | length == 0 + + +# Warning conditions: +# - At least one IMDB_AFL100 was found +# - No IMDB_AFL100__ was found +# Examples: +# - IMDB_SERVER100_122_35-10009569.SAR - Maintenance Revision 122.35 (SPS12) for HANA DB 1.00 +# - IMDB_AFL100_122P_3500-10012328.SAR - SAP HANA AFL 1.0 Revision 122.3500 only for HANA DB 122.35 +- name: Relation Check - SAP HANA 1.00 - IMDB_SERVER and IMDB_AFL + ansible.builtin.fail: + msg: | + Warning: Incompatible SAP HANA AFL 1.0 files were found. + Ensure that correct version is selected: {{ __sap_software_download_sap_hana_1_version + }}.{{ __sap_software_download_sap_hana_1_revision }} + + Expected file pattern: IMDB_AFL100_{{ __sap_software_download_sap_hana_1_version }}*_{{ __sap_software_download_sap_hana_1_revision }}* + Actual files detected: {{ __sap_software_download_sap_hana_1_afl_list | unique | join(', ') }} + vars: + __sap_software_download_sap_hana_1_afl_list: + "{{ __sap_software_download_files | select('match', '^IMDB_AFL100.*') | list }}" + __sap_software_download_sap_hana_1_afl_list_filtered: + "{{ sap_software_download_files | select('match', '^IMDB_AFL100_.*' + ~ __sap_software_download_sap_hana_1_version ~ '.*_' ~ __sap_software_download_sap_hana_1_revision) | list }}" + ignore_errors: "{{ sap_software_download_ignore_relation_warning | d(false) }}" + when: + - __sap_software_download_sap_hana | regex_search('^IMDB_SERVER1') + - __sap_software_download_sap_hana_1_afl_list | length > 0 + - __sap_software_download_sap_hana_1_afl_list_filtered | length == 0 + + +# Warning conditions: +# - At least one IMDB_AFL20 was found +# - No IMDB_AFL20_ was found +# Examples: + # - IMDB_SERVER20_084_0-80002031.SAR - Revision 2.00.084.0 (SPS08) for HANA DB 2.0 + # - IMDB_AFL20_084_1-80001894.SAR - SAP HANA AFL Rev 84.1 only for HANA 2.0 Rev 84 +- name: Relation Check - SAP HANA 2.00 - IMDB_SERVER and IMDB_AFL + ansible.builtin.fail: + msg: | + Warning: Incompatible SAP HANA AFL 2.0 files were found. + Ensure that correct version is selected: {{ __sap_software_download_sap_hana_2_version }} + + Expected file pattern: IMDB_AFL20_{{ __sap_software_download_sap_hana_2_version }}* + Actual files detected: {{ __sap_software_download_sap_hana_2_afl_list | unique | join(', ') }} + vars: + __sap_software_download_sap_hana_2_afl_list: + "{{ __sap_software_download_files | select('match', '^IMDB_AFL20.*') | list }}" + __sap_software_download_sap_hana_2_afl_list_filtered: + "{{ sap_software_download_files | select('match', '^IMDB_AFL20_.*' ~ __sap_software_download_sap_hana_2_version) | list }}" + ignore_errors: "{{ sap_software_download_ignore_relation_warning | d(false) }}" + when: + - __sap_software_download_sap_hana | regex_search('^IMDB_SERVER2') + - __sap_software_download_sap_hana_2_afl_list | length > 0 + - __sap_software_download_sap_hana_2_afl_list_filtered | length == 0 + + +# Warning conditions: +# - At least one IMDB_CLIENT was found +# - No IMDB_CLIENT1* was found +# Examples: + # - IMDB_SERVER100_122_35-10009569.SAR - Maintenance Revision 122.35 (SPS12) for HANA DB 1.00 + # - IMDB_CLIENT100_120_140-10009663.SAR - Revision 120 for SAP HANA CLIENT 1.00 +- name: Relation Check - SAP HANA 1.00 - IMDB_SERVER and IMDB_CLIENT + ansible.builtin.fail: + msg: | + Warning: SAP HANA CLIENT 1.00 was not found. + + Expected file pattern: IMDB_CLIENT100_* + Actual files detected: {{ __sap_software_download_sap_hana_client_list | unique | join(', ') }} + vars: + __sap_software_download_sap_hana_client_list: + "{{ __sap_software_download_files | select('match', '^IMDB_CLIENT.*') | list }}" + ignore_errors: "{{ sap_software_download_ignore_relation_warning | d(false) }}" + when: + - __sap_software_download_sap_hana | regex_search('^IMDB_SERVER1') + and __sap_software_download_sap_hana_client_list | length > 0 + and __sap_software_download_sap_hana_client_list | select('match', '^IMDB_CLIENT1.*') | length == 0 + + +# Warning conditions: +# - At least one IMDB_CLIENT was found +# - No IMDB_CLIENT2* was found +# Examples: + # - IMDB_SERVER20_084_0-80002031.SAR - Revision 2.00.084.0 (SPS08) for HANA DB 2.0 + # - IMDB_CLIENT20_024_21-80002082.SAR - SAP HANA CLIENT Version 2.24 +- name: Relation Check - SAP HANA 2.00 - IMDB_SERVER and IMDB_CLIENT + ansible.builtin.fail: + msg: | + Warning: SAP HANA CLIENT 2.00 was not found. + + Expected file pattern: IMDB_CLIENT20_* + Actual files detected: {{ __sap_software_download_sap_hana_client_list | unique | join(', ') }} + vars: + __sap_software_download_sap_hana_client_list: + "{{ __sap_software_download_files | select('match', '^IMDB_CLIENT.*') | list }}" + ignore_errors: "{{ sap_software_download_ignore_relation_warning | d(false) }}" + when: + - __sap_software_download_sap_hana | regex_search('^IMDB_SERVER2') + and __sap_software_download_sap_hana_client_list | length > 0 + and __sap_software_download_sap_hana_client_list | select('match', '^IMDB_CLIENT2.*') | length == 0 From 62907170e3fb34a1419aa78f3219e0a4739ff3e4 Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Mon, 31 Mar 2025 12:45:20 +0200 Subject: [PATCH 12/26] feat: add dry run connection check, improve verbosity --- roles/sap_software_download/README.md | 27 ++- roles/sap_software_download/defaults/main.yml | 12 +- .../meta/argument_spec.yml | 26 ++- .../tasks/download_files.yml | 6 +- .../tasks/download_plan.yml | 10 +- roles/sap_software_download/tasks/main.yml | 21 ++- .../tasks/pre_steps/01_include_variables.yml | 12 +- .../pre_steps/03_validate_credentials.yml | 49 +++++ ...t_plan_files.yml => 04_get_plan_files.yml} | 22 +-- ...elations.yml => 05_validate_relations.yml} | 36 ++-- .../tasks/pre_steps/05_validate_sap_hana.yml | 170 ------------------ .../pre_steps/06_validate_sap_hana_1.yml | 87 +++++++++ .../pre_steps/07_validate_sap_hana_2.yml | 80 +++++++++ 13 files changed, 321 insertions(+), 237 deletions(-) create mode 100644 roles/sap_software_download/tasks/pre_steps/03_validate_credentials.yml rename roles/sap_software_download/tasks/pre_steps/{03_get_plan_files.yml => 04_get_plan_files.yml} (73%) rename roles/sap_software_download/tasks/pre_steps/{04_validate_relations.yml => 05_validate_relations.yml} (73%) delete mode 100644 roles/sap_software_download/tasks/pre_steps/05_validate_sap_hana.yml create mode 100644 roles/sap_software_download/tasks/pre_steps/06_validate_sap_hana_1.yml create mode 100644 roles/sap_software_download/tasks/pre_steps/07_validate_sap_hana_2.yml diff --git a/roles/sap_software_download/README.md b/roles/sap_software_download/README.md index 9b95326..a85a387 100644 --- a/roles/sap_software_download/README.md +++ b/roles/sap_software_download/README.md @@ -34,11 +34,11 @@ The target node must meet the following requirements: 2. **Python Environment Preparation:** The role prepares the Python environment: * **Virtual Environment (Default):** A temporary Python virtual environment is created, and all necessary dependencies are installed within it. * **System Environment:** Alternatively, if `sap_software_download_use_venv` is set to `false`, dependencies are installed directly into the system's default Python environment. -3. **Maintenance Plan File List:** If the `sap_software_download_plan` variable is provided, the role retrieves the list of files associated with the specified Maintenance Plan transaction. -4. **File Relationship Validation:** If `sap_software_download_validate_relations` is `true`, the role performs validation checks on the relationships between the files to be downloaded. +3. **Maintenance Plan File List:** If the `sap_software_download_mp_transaction` variable is provided, the role retrieves the list of files associated with the specified Maintenance Plan transaction. +4. **File Relationship Validation:** If `sap_software_download_validate_relationships` is `true`, the role performs validation checks on the relationships between the files to be downloaded. * **Alternative File Search:** If `sap_software_download_find_alternatives` is `true`, the role will search for alternative files if the requested files are not found. * More information about validation logic is available at [Explanation of relationship validation logic](#explanation-of-relationship-validation-logic) -5. **Maintenance Plan File Download:** If `sap_software_download_plan` is provided, the role downloads the files associated with the Maintenance Plan. +5. **Maintenance Plan File Download:** If `sap_software_download_mp_transaction` is provided, the role downloads the files associated with the Maintenance Plan. 6. **Direct File Download:** If `sap_software_download_files` is provided, the role downloads the specified files. 7. **Virtual Environment Cleanup:** If a temporary Python virtual environment was used, it is removed. @@ -79,7 +79,7 @@ Download of SAP Software files using Maintenance Plan sap_software_download_suser_id: "Enter SAP S-User ID" sap_software_download_suser_password: "Enter SAP S-User Password" sap_software_download_directory: "/software" - sap_software_download_plan: 'Transaction Name or Display ID from Maintenance Planner' + sap_software_download_mp_transaction: 'Transaction Name or Display ID from Maintenance Planner' ``` @@ -100,13 +100,13 @@ Example for SAP HANA Database Server 2.0 with LCAPPS and AFL.
- A file like `IMDB_LCAPPS_2084_0-20010426.SAR` is present. In this case, validation will pass because the LCAPPS version is compatible with the HANA revision. - No file starting with `IMDB_LCAPPS_2084` is present, but there are files starting with `IMDB_LCAPPS_2`.
This indicates a mismatch because the LCAPPS version is not compatible with the specific HANA revision (084) found in step 2.
- In this case, validation will fail. This can be prevented by setting `sap_software_download_ignore_relation_warning` to `true`. + In this case, validation will fail. This can be prevented by setting `sap_software_download_ignore_relationship_warning` to `true`. 6. All files are examined for files starting with `IMDB_AFL20` (indicating AFL for HANA 2.0). Then the list is filtered to only include files starting with `IMDB_AFL20_084` (indicating AFL for HANA 2.0 revision 084). 7. Validation will have two outcomes: - A file like `IMDB_AFL20_084_1-80001894.SAR` is present. In this case, validation will pass because the AFL version is compatible with the HANA revision. - No file starting with `IMDB_AFL20_084` is present, but there are files starting with `IMDB_AFL20`.
This indicates a mismatch because the AFL version is not compatible with the specific HANA revision (084) found in step 2.
- In this case, validation will fail. This can be prevented by setting `sap_software_download_ignore_relation_warning` to `true`. + In this case, validation will fail. This can be prevented by setting `sap_software_download_ignore_relationship_warning` to `true`. This validation example checks major and minor release (SPS and Revision), but it does not validate patch version. @@ -163,7 +163,7 @@ The password for the SAP S-User specified in `sap_software_download_suser_id`. -### sap_software_download_plan +### sap_software_download_mp_transaction - _Type:_ `string`
The name or display ID of a transaction from the SAP Maintenance Planner.
@@ -182,7 +182,7 @@ If set to `false`, the role will not search for alternatives.
The directory where downloaded SAP software files will be stored.
-### sap_software_download_validate_relations +### sap_software_download_validate_relationships - _Type:_ `bool`
- _Default:_ `true`
@@ -207,7 +207,7 @@ Determines whether to ignore errors when a specified Maintenance Plan transactio If set to `true` and a Maintenance Plan is not found, the role will continue execution, downloading any files specified in `sap_software_download_files`.
If set to `false`, the role will fail if the specified Maintenance Plan is not found.
-### sap_software_download_ignore_relation_warning +### sap_software_download_ignore_relationship_warning - _Type:_ `bool`
- _Default:_ `false`
@@ -215,6 +215,15 @@ Determines whether to ignore warnings during file relationship validation.
If set to `true`, the role will continue execution even if there are warnings during the validation of file relationships.
If set to `false`, the role will fail if any warnings are encountered during file relationship validation.
+### sap_software_download_ignore_validate_credentials +- _Type:_ `bool`
+- _Default:_ `false`
+ +Determines whether to ignore validate credentials task.
+Disabling this check can lead to locked account, if password is incorrect.
+If set to `true`, the role will continue execution without validating S-User credentials.
+If set to `false`, the role will execute dry run to validate S-User credentials.
+ ### sap_software_download_deduplicate - _Type:_ `string`
diff --git a/roles/sap_software_download/defaults/main.yml b/roles/sap_software_download/defaults/main.yml index d844f9b..13787f9 100644 --- a/roles/sap_software_download/defaults/main.yml +++ b/roles/sap_software_download/defaults/main.yml @@ -32,7 +32,7 @@ sap_software_download_files: [] # The name or display ID of a transaction from the SAP Maintenance Planner. # If provided, the role will download all files associated with this Maintenance Plan transaction. -sap_software_download_plan: '' +sap_software_download_mp_transaction: '' # Enables searching for alternative files if the requested file is not found. # Only applies to files specified in `sap_software_download_files`. @@ -46,7 +46,7 @@ sap_software_download_directory: '/software' # Only applies to files specified in `sap_software_download_files`. # If set to `false`, no relationship validation will be performed. # Example: Verify version of IMDB_LCAPPS against IMDB_SERVER if IMDB_SERVER was found. -sap_software_download_validate_relations: true +sap_software_download_validate_relationships: true # Determines whether to ignore errors when a requested file is not found. # If set to `true`, the role will continue execution and download other files, even if some files are not found. @@ -62,7 +62,13 @@ sap_software_download_ignore_plan_not_found: false # Determines whether to ignore warnings during file relationship validation. # If set to `true`, the role will continue execution even if there are warnings during the validation of file relationships. # If set to `false`, the role will fail if any warnings are encountered during file relationship validation. -sap_software_download_ignore_relation_warning: false +sap_software_download_ignore_relationship_warning: false + +# Determines whether to ignore validate credentials task. +# Disabling this check can lead to locked account, if password is incorrect. +# If set to `true`, the role will continue execution without validating S-User credentials. +# If set to `false`, the role will execute dry run to validate S-User credentials. +sap_software_download_ignore_validate_credentials: false # Specifies how to handle duplicate file results when using `sap_software_download_files`. # If multiple files with the same name are found, this setting determines which one to download. diff --git a/roles/sap_software_download/meta/argument_spec.yml b/roles/sap_software_download/meta/argument_spec.yml index 19016b9..dec55ce 100644 --- a/roles/sap_software_download/meta/argument_spec.yml +++ b/roles/sap_software_download/meta/argument_spec.yml @@ -34,7 +34,7 @@ argument_specs: sap_software_download_use_venv: type: bool - required: true + required: false default: true description: - Determines whether to execute the role within a Python virtual environment. @@ -62,7 +62,7 @@ argument_specs: description: - A list of SAP software file names to download. - sap_software_download_plan: + sap_software_download_mp_transaction: type: str required: false default: "" @@ -86,9 +86,9 @@ argument_specs: description: - The directory where downloaded SAP software files will be stored. - sap_software_download_validate_relations: + sap_software_download_validate_relationships: type: bool - required: true + required: false default: true description: - Enables validation of relationships between SAP software files. @@ -98,7 +98,7 @@ argument_specs: sap_software_download_ignore_file_not_found: type: bool - required: true + required: false default: false description: - Determines whether to ignore errors when a requested file is not found. @@ -107,7 +107,7 @@ argument_specs: sap_software_download_ignore_plan_not_found: type: bool - required: true + required: false default: false description: - Determines whether to ignore errors when a specified Maintenance Plan transaction is not found. @@ -115,12 +115,22 @@ argument_specs: - downloading any files specified in `sap_software_download_files`. - If set to `false`, the role will fail if the specified Maintenance Plan is not found. - sap_software_download_ignore_relation_warning: + sap_software_download_ignore_relationship_warning: type: bool - required: true + required: false + default: false + description: + - Determines whether to ignore warnings during file relationship validation. + - If set to `true`, the role will continue execution even if there are warnings during the validation of file relationships. + - If set to `false`, the role will fail if any warnings are encountered during file relationship validation. + + sap_software_download_ignore_validate_credentials: + type: bool + required: false default: false description: - Determines whether to ignore warnings during file relationship validation. + - Disabling this check can lead to locked account, if password is incorrect. - If set to `true`, the role will continue execution even if there are warnings during the validation of file relationships. - If set to `false`, the role will fail if any warnings are encountered during file relationship validation. diff --git a/roles/sap_software_download/tasks/download_files.yml b/roles/sap_software_download/tasks/download_files.yml index 0e7ba73..6afcc76 100644 --- a/roles/sap_software_download/tasks/download_files.yml +++ b/roles/sap_software_download/tasks/download_files.yml @@ -25,7 +25,7 @@ ignore_errors: true # Errors are ignored and validated afterwards -- name: Download - SAP Software Files - Get files with default Python +- name: Download - SAP Software Files - Get files with Python system default community.sap_launchpad.software_center_download: suser_id: "{{ sap_software_download_suser_id }}" suser_password: "{{ sap_software_download_suser_password }}" @@ -50,7 +50,7 @@ __sap_software_download_files_results: "{{ __sap_software_download_files_results_venv if sap_software_download_use_venv | d(true) else __sap_software_download_files_results_default }}" -- name: Relation Validation - Show failed results in dry run +- name: Download - SAP Software Files - Show failed results ansible.builtin.fail: msg: | Download failed for following files: {{ __failed_items | map(attribute='item') | list | join(', ') }} @@ -60,5 +60,5 @@ __failed_items: "{{ __sap_software_download_files_results.results | selectattr('failed', 'defined') | selectattr('failed', 'true') }}" when: - - not sap_software_download_ignore_file_not_found + - not sap_software_download_ignore_file_not_found | d(false) - __failed_items | length > 0 diff --git a/roles/sap_software_download/tasks/download_plan.yml b/roles/sap_software_download/tasks/download_plan.yml index 0a33c5b..34b4d31 100644 --- a/roles/sap_software_download/tasks/download_plan.yml +++ b/roles/sap_software_download/tasks/download_plan.yml @@ -9,7 +9,7 @@ download_filename: "{{ item.Filename }}" download_path: "{{ sap_software_download_directory }}" # Loop condition acts as when conditional - loop: "{{ __sap_software_download_plan_results.download_basket if sap_software_download_use_venv | d(true) else [] }}" + loop: "{{ __sap_software_download_mp_transaction_results.download_basket if sap_software_download_use_venv | d(true) else [] }}" loop_control: label: "{{ item.Filename }} : {{ __sap_software_download_files_plan_results_venv.msg | d('') }}" register: __sap_software_download_files_plan_results_venv @@ -24,7 +24,7 @@ ignore_errors: true # Errors are ignored and validated afterwards -- name: Download - Maintenance Plan - Get files with default Python +- name: Download - Maintenance Plan - Get files with Python system default community.sap_launchpad.software_center_download: suser_id: "{{ sap_software_download_suser_id }}" suser_password: "{{ sap_software_download_suser_password }}" @@ -32,7 +32,7 @@ download_filename: "{{ item.Filename }}" download_path: "{{ sap_software_download_directory }}" # Loop condition acts as when conditional - loop: "{{ __sap_software_download_plan_results.download_basket if not sap_software_download_use_venv | d(true) else [] }}" + loop: "{{ __sap_software_download_mp_transaction_results.download_basket if not sap_software_download_use_venv | d(true) else [] }}" loop_control: label: "{{ item.Filename }} : {{ __sap_software_download_files_plan_results_default.msg | d('') }}" register: __sap_software_download_files_plan_results_default @@ -51,11 +51,11 @@ - name: Download - Maintenance Plan - Show failed results ansible.builtin.fail: msg: | - Maintenance Plan file(s) not found: {{ __failed_items | map(attribute='item') | list | join(', ') }} + Maintenance Plan file(s) not found: {{ __failed_items | map(attribute='Filename') | list | join(', ') }} Verify your Maintenance Plan on SAP Launchpad before retrying. vars: __failed_items: "{{ __sap_software_download_files_plan_results.results | selectattr('failed', 'defined') | selectattr('failed', 'true') }}" when: - - not sap_software_download_ignore_file_not_found + - not sap_software_download_ignore_file_not_found | d(false) - __failed_items | length > 0 diff --git a/roles/sap_software_download/tasks/main.yml b/roles/sap_software_download/tasks/main.yml index 586ffca..299dcf5 100644 --- a/roles/sap_software_download/tasks/main.yml +++ b/roles/sap_software_download/tasks/main.yml @@ -9,28 +9,33 @@ ansible.builtin.include_tasks: file: pre_steps/02_prepare_python_environment.yml +- name: SAP Software Download - Pre-Steps - Validate user credentials + ansible.builtin.include_tasks: + file: pre_steps/03_validate_credentials.yml + when: not sap_software_download_ignore_validate_credentials | d(false) + - name: SAP Software Download - Pre-Steps - Find files for Maintenance Plan ansible.builtin.include_tasks: - file: pre_steps/03_get_plan_files.yml + file: pre_steps/04_get_plan_files.yml when: - - sap_software_download_plan | length > 0 + - sap_software_download_mp_transaction | length > 0 # NOTE: We do not validate files generated by Maintenance Plan. - name: SAP Software Download - Pre-Steps - Check related SAP Software combinations ansible.builtin.include_tasks: - file: pre_steps/04_validate_relations.yml + file: pre_steps/05_validate_relations.yml when: - sap_software_download_files | length > 0 - - sap_software_download_validate_relations + - sap_software_download_validate_relationships -- name: SAP Software Download - Download - Maintenance Plan {{ sap_software_download_plan | d('') }} +- name: SAP Software Download - Download - Maintenance Plan {{ sap_software_download_mp_transaction | d('') }} ansible.builtin.include_tasks: file: download_plan.yml when: - - sap_software_download_plan | length > 0 - - __sap_software_download_plan_results.download_basket is defined - and __sap_software_download_plan_results.download_basket | length > 0 + - sap_software_download_mp_transaction | length > 0 + - __sap_software_download_mp_transaction_results.download_basket is defined + and __sap_software_download_mp_transaction_results.download_basket | length > 0 - name: SAP Software Download - Download - Files in sap_software_download_files ansible.builtin.include_tasks: diff --git a/roles/sap_software_download/tasks/pre_steps/01_include_variables.yml b/roles/sap_software_download/tasks/pre_steps/01_include_variables.yml index 35c90a7..f8611db 100644 --- a/roles/sap_software_download/tasks/pre_steps/01_include_variables.yml +++ b/roles/sap_software_download/tasks/pre_steps/01_include_variables.yml @@ -35,9 +35,9 @@ ansible.builtin.assert: that: - (sap_software_download_files is defined and sap_software_download_files | length > 0) - or (sap_software_download_plan is defined and sap_software_download_plan | length > 0) + or (sap_software_download_mp_transaction is defined and sap_software_download_mp_transaction | length > 0) fail_msg: | - Neither `sap_software_download_files` or `sap_software_download_plan` are valid. + Neither `sap_software_download_files` or `sap_software_download_mp_transaction` are valid. Ensure that it at least one of them is valid. - name: "Pre-Steps - Assert that download directory was provided" @@ -52,10 +52,10 @@ - name: "Pre-Steps - Verify variable: sap_software_download_use_venv" ansible.builtin.assert: that: - - sap_software_download_use_venv is defined - sap_software_download_use_venv is boolean fail_msg: | - Variable `sap_software_download_use_venv` is not boolean. + Variable `sap_software_download_use_venv` is not boolean. + when: sap_software_download_use_venv is defined - name: "Pre-Steps - Verify variable: sap_software_download_python_interpreter" ansible.builtin.assert: @@ -98,10 +98,10 @@ - name: "Pre-Steps - Verify variable: sap_software_download_find_alternatives" ansible.builtin.assert: that: - - sap_software_download_find_alternatives is defined - sap_software_download_find_alternatives is boolean fail_msg: | - Variable `sap_software_download_find_alternatives` is not boolean. + Variable `sap_software_download_find_alternatives` is not boolean. + when: sap_software_download_find_alternatives is defined - name: "Pre-Steps - Verify variable: sap_software_download_deduplicate" ansible.builtin.assert: diff --git a/roles/sap_software_download/tasks/pre_steps/03_validate_credentials.yml b/roles/sap_software_download/tasks/pre_steps/03_validate_credentials.yml new file mode 100644 index 0000000..ef63819 --- /dev/null +++ b/roles/sap_software_download/tasks/pre_steps/03_validate_credentials.yml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: Apache-2.0 +--- +# This task attempts dry run to check SAPCAR file in order to validate provided credentials. + +- name: Validate Credentials - Dry run to download test file with Python venv + when: sap_software_download_use_venv | d(true) + community.sap_launchpad.software_center_download: + suser_id: "{{ sap_software_download_suser_id }}" + suser_password: "{{ sap_software_download_suser_password }}" + search_query: "SAPCAR" + download_path: "{{ sap_software_download_directory }}" + search_alternatives: true + deduplicate: "last" + dry_run: true + register: __sap_software_download_validate_credentials_venv + environment: + PATH: "{{ __sap_software_download_venv.path }}/bin:{{ ansible_env.PATH }}" + PYTHONPATH: "{{ __sap_software_download_venv.path }}/lib/{{ sap_software_download_python_interpreter }}/site-packages" + VIRTUAL_ENV: "{{ __sap_software_download_venv.path }}" + vars: + ansible_python_interpreter: "{{ __sap_software_download_venv.path ~ '/bin/' ~ sap_software_download_python_interpreter }}" + ignore_errors: true # Errors are ignored and validated afterwards + +- name: Validate Credentials - Dry run to download test file with Python system default + when: not sap_software_download_use_venv | d(true) + community.sap_launchpad.software_center_download: + suser_id: "{{ sap_software_download_suser_id }}" + suser_password: "{{ sap_software_download_suser_password }}" + search_query: "SAPCAR" + download_path: "{{ sap_software_download_directory }}" + search_alternatives: true + deduplicate: "last" + dry_run: true + register: __sap_software_download_validate_credentials_default + vars: + ansible_python_interpreter: "{{ '/usr/bin/' ~ sap_software_download_python_interpreter }}" + ignore_errors: true # Errors are ignored and validated afterwards + +- name: Validate Credentials - Show failed results + ansible.builtin.fail: + msg: | + Validation of provided S-User credentials failed. + Ensure correct S-User credentials are provided in: + `sap_software_download_suser_id` and `sap_software_download_suser_password` + when: + - (sap_software_download_use_venv | d(true) + and __sap_software_download_validate_credentials_venv.failed is defined and __sap_software_download_validate_credentials_venv.failed) + or (not sap_software_download_use_venv | d(true) + and __sap_software_download_validate_credentials_default is defined and __sap_software_download_validate_credentials_default.failed) diff --git a/roles/sap_software_download/tasks/pre_steps/03_get_plan_files.yml b/roles/sap_software_download/tasks/pre_steps/04_get_plan_files.yml similarity index 73% rename from roles/sap_software_download/tasks/pre_steps/03_get_plan_files.yml rename to roles/sap_software_download/tasks/pre_steps/04_get_plan_files.yml index 46707f4..11e8c4c 100644 --- a/roles/sap_software_download/tasks/pre_steps/03_get_plan_files.yml +++ b/roles/sap_software_download/tasks/pre_steps/04_get_plan_files.yml @@ -6,8 +6,8 @@ community.sap_launchpad.maintenance_planner_files: suser_id: "{{ sap_software_download_suser_id }}" suser_password: "{{ sap_software_download_suser_password }}" - transaction_name: "{{ sap_software_download_plan }}" - register: __sap_software_download_plan_results_venv + transaction_name: "{{ sap_software_download_mp_transaction }}" + register: __sap_software_download_mp_transaction_results_venv retries: 1 environment: PATH: "{{ __sap_software_download_venv.path }}/bin:{{ ansible_env.PATH }}" @@ -23,8 +23,8 @@ community.sap_launchpad.maintenance_planner_files: suser_id: "{{ sap_software_download_suser_id }}" suser_password: "{{ sap_software_download_suser_password }}" - transaction_name: "{{ sap_software_download_plan }}" - register: __sap_software_download_plan_results_default + transaction_name: "{{ sap_software_download_mp_transaction }}" + register: __sap_software_download_mp_transaction_results_default retries: 1 vars: ansible_python_interpreter: "{{ '/usr/bin/' ~ sap_software_download_python_interpreter }}" @@ -33,19 +33,19 @@ - name: Maintenance Plan - Set fact with maintenance_planner_files output ansible.builtin.set_fact: - __sap_software_download_plan_results: "{{ __sap_software_download_plan_results_venv - if sap_software_download_use_venv | d(true) else __sap_software_download_plan_results_default }}" + __sap_software_download_mp_transaction_results: "{{ __sap_software_download_mp_transaction_results_venv + if sap_software_download_use_venv | d(true) else __sap_software_download_mp_transaction_results_default }}" - name: Maintenance Plan - Show failed results ansible.builtin.fail: msg: | Maintenance Plan was not found. - Either ensure correct value in `sap_software_download_plan` + Either ensure correct value in `sap_software_download_mp_transaction` or ignore this error with `sap_software_download_ignore_plan_not_found` set to `true`. when: - - not sap_software_download_ignore_plan_not_found - - __sap_software_download_plan_results.failed + - not sap_software_download_ignore_plan_not_found | d(false) + - __sap_software_download_mp_transaction_results.failed - name: Maintenance Plan - Show ignored failed results ansible.builtin.debug: @@ -53,5 +53,5 @@ Maintenance Plan was not found. Error was ignored with `sap_software_download_ignore_plan_not_found` set to `true`. when: - - sap_software_download_ignore_plan_not_found - - __sap_software_download_plan_results.failed + - sap_software_download_ignore_plan_not_found | d(false) + - __sap_software_download_mp_transaction_results.failed diff --git a/roles/sap_software_download/tasks/pre_steps/04_validate_relations.yml b/roles/sap_software_download/tasks/pre_steps/05_validate_relations.yml similarity index 73% rename from roles/sap_software_download/tasks/pre_steps/04_validate_relations.yml rename to roles/sap_software_download/tasks/pre_steps/05_validate_relations.yml index f18d87d..b0b0915 100644 --- a/roles/sap_software_download/tasks/pre_steps/04_validate_relations.yml +++ b/roles/sap_software_download/tasks/pre_steps/05_validate_relations.yml @@ -1,11 +1,11 @@ # SPDX-License-Identifier: Apache-2.0 --- -- name: Block - Relation Validation - Check availability of files (with alternatives) +- name: Block - Relationship Validation - Check availability of files (with alternatives) when: sap_software_download_find_alternatives block: - - name: Relation Validation - Check availability of files with Python venv + - name: Relationship Validation - Dry run to check availability of files with Python venv community.sap_launchpad.software_center_download: suser_id: "{{ sap_software_download_suser_id }}" suser_password: "{{ sap_software_download_suser_password }}" @@ -30,7 +30,7 @@ ignore_errors: true # Errors are ignored and validated afterwards - - name: Relation Validation - Check availability of files with default Python + - name: Relationship Validation - Dry run to check availability of files with Python system default community.sap_launchpad.software_center_download: suser_id: "{{ sap_software_download_suser_id }}" suser_password: "{{ sap_software_download_suser_password }}" @@ -51,40 +51,48 @@ ignore_errors: true # Errors are ignored and validated afterwards - - name: Relation Validation - Set fact with software_center_download output + - name: Relationship Validation - Set fact with software_center_download output ansible.builtin.set_fact: __sap_software_download_files_results_dryrun: "{{ __sap_software_download_files_results_dryrun_venv if sap_software_download_use_venv | d(true) else __sap_software_download_files_results_dryrun_default }}" - - name: Relation Validation - Show failed results + - name: Relationship Validation - Show failed results ansible.builtin.fail: msg: | - Relation validation failed because following files were not found: {{ __failed_items | map(attribute='item') | list | join(', ') }} + Relationship validation failed because following files were not found: {{ __failed_items | map(attribute='item') | list | join(', ') }} Either ensure correct list of files in `sap_software_download_files` or ignore this error with `sap_software_download_ignore_file_not_found` set to `true`. vars: __failed_items: "{{ __sap_software_download_files_results_dryrun.results | selectattr('failed', 'defined') | selectattr('failed', 'true') }}" when: - - not sap_software_download_ignore_file_not_found + - not sap_software_download_ignore_file_not_found | d(false) - __failed_items | length > 0 -- name: Relation Validation - Define list of files +- name: Relationship Validation - Define list of files ansible.builtin.set_fact: __sap_software_download_files: "{{ sap_software_download_files if not sap_software_download_find_alternatives else __sap_software_download_files_results_dryrun.results | selectattr('failed', 'false')| map(attribute='filename') | list | unique | d([])}}" -- name: Relation Validation - SAP HANA - Multiple IMDB_SERVER files found +- name: Relationship Validation - SAP HANA - Multiple IMDB_SERVER files found ansible.builtin.debug: - msg: Warning - Multiple SAP HANA Database (IMDB_SERVER) found, which can result in inaccurate relation validation! + msg: Warning - Multiple SAP HANA Database (IMDB_SERVER) found, which can result in inaccurate relationship validation! when: __sap_software_download_files | select('match', '^IMDB_SERVER.*') | list | length > 1 -- name: Relation Validation - Include tasks for SAP HANA relation validation - when: __sap_software_download_files | select('match', '^IMDB_SERVER.*') | list | length > 0 +- name: Relationship Validation - Include tasks for SAP HANA 1.0 relationship validation + when: __sap_software_download_files | select('match', '^IMDB_SERVER1.*') | list | length > 0 ansible.builtin.include_tasks: - file: pre_steps/05_validate_sap_hana.yml - loop: "{{ __sap_software_download_files | select('match', '^IMDB_SERVER.*') | list }}" + file: pre_steps/06_validate_sap_hana_1.yml + loop: "{{ __sap_software_download_files | select('match', '^IMDB_SERVER1.*') | list }}" + loop_control: + loop_var: __sap_software_download_sap_hana + +- name: Relationship Validation - Include tasks for SAP HANA 2.0 relationship validation + when: __sap_software_download_files | select('match', '^IMDB_SERVER2.*') | list | length > 0 + ansible.builtin.include_tasks: + file: pre_steps/07_validate_sap_hana_2.yml + loop: "{{ __sap_software_download_files | select('match', '^IMDB_SERVER2.*') | list }}" loop_control: loop_var: __sap_software_download_sap_hana diff --git a/roles/sap_software_download/tasks/pre_steps/05_validate_sap_hana.yml b/roles/sap_software_download/tasks/pre_steps/05_validate_sap_hana.yml deleted file mode 100644 index 7500805..0000000 --- a/roles/sap_software_download/tasks/pre_steps/05_validate_sap_hana.yml +++ /dev/null @@ -1,170 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 ---- -# Logic behind relation validation: -# - Known variations for files are validated in dedicated tasks -# - Each SAP HANA version have different handling due to different versioning - -- name: Relation Validation - SAP HANA - Set HANA variables - ansible.builtin.set_fact: - # "{{ '1' if __sap_software_download_sap_hana | regex_search('^IMDB_SERVER1') else '2' }}" - __sap_software_download_sap_hana_1_version: - "{{ __sap_software_download_sap_hana.split('_')[2] | regex_replace('[^0-9]', '') }}" # 122 for IMDB_SERVER100_122_35-10009569.SAR - __sap_software_download_sap_hana_1_revision: - "{{ __sap_software_download_sap_hana.split('_')[3].split('-')[0] | regex_replace('[^0-9]', '') }}" # 35 for IMDB_SERVER100_122_35-10009569.SAR - - __sap_software_download_sap_hana_2_version: - "{{ __sap_software_download_sap_hana.split('_')[2] }}" # 084 for IMDB_SERVER20_084_0-80002031.SAR - - -# Warning conditions: -# - At least one IMDB_LCAPPS_1 was found -# - No IMDB_LCAPPS__ was found -# Examples: -# - IMDB_SERVER100_122_35-10009569.SAR - Maintenance Revision 122.35 (SPS12) for HANA DB 1.00 -# - IMDB_LCAPPS_122P_3500-20010426.SAR - LCAPPS for HANA 1.00.122.35 Build 100.47 PL 017 -- name: Relation Check - SAP HANA 1.00 - IMDB_SERVER and IMDB_LCAPPS - ansible.builtin.fail: - msg: | - Warning: Incompatible SAP HANA 1.0 LCAPPS files were found. - Ensure that correct version is selected: {{ __sap_software_download_sap_hana_1_version - }}.{{ __sap_software_download_sap_hana_1_revision }} - - Expected file pattern: IMDB_LCAPPS_{{ __sap_software_download_sap_hana_1_version }}*_{{ __sap_software_download_sap_hana_1_revision }}* - Actual files detected: {{ __sap_software_download_sap_hana_1_lcapps_list | unique | join(', ') }} - vars: - __sap_software_download_sap_hana_1_lcapps_list: - "{{ __sap_software_download_files | select('match', '^IMDB_LCAPPS_1.*') | list }}" - __sap_software_download_sap_hana_1_lcapps_list_filtered: - "{{ sap_software_download_files | select('match', '^IMDB_LCAPPS_.*' - ~ __sap_software_download_sap_hana_1_version ~ '.*_' ~ __sap_software_download_sap_hana_1_revision) | list }}" - ignore_errors: "{{ sap_software_download_ignore_relation_warning | d(false) }}" - when: - - __sap_software_download_sap_hana | regex_search('^IMDB_SERVER1') - - __sap_software_download_sap_hana_1_lcapps_list | length > 0 - - __sap_software_download_sap_hana_1_lcapps_list_filtered | length == 0 - - -# Warning conditions: -# - At least one IMDB_LCAPPS_2 was found -# - No IMDB_LCAPPS_2 was found -# Examples: - # - IMDB_SERVER20_084_0-80002031.SAR - Revision 2.00.084.0 (SPS08) for HANA DB 2.0 - # - IMDB_LCAPPS_2084_0-20010426.SAR - LCAPPS for HANA 2.0 Rev 84 Build 101.19 PL 007 -- name: Relation Check - SAP HANA 2.00 - IMDB_SERVER and IMDB_LCAPPS - ansible.builtin.fail: - msg: | - Warning: Incompatible SAP HANA 2.0 LCAPPS files were found. - Ensure that correct version is selected: {{ __sap_software_download_sap_hana_2_version }} - - Expected file pattern: IMDB_LCAPPS_2{{ __sap_software_download_sap_hana_2_version }}* - Actual files detected: {{ __sap_software_download_sap_hana_2_lcapps_list | unique | join(', ') }} - vars: - __sap_software_download_sap_hana_2_lcapps_list: - "{{ __sap_software_download_files | select('match', '^IMDB_LCAPPS_2.*') | list }}" - __sap_software_download_sap_hana_2_lcapps_list_filtered: - "{{ sap_software_download_files | select('match', '^IMDB_LCAPPS_2' ~ __sap_software_download_sap_hana_2_version) | list }}" - ignore_errors: "{{ sap_software_download_ignore_relation_warning | d(false) }}" - when: - - __sap_software_download_sap_hana | regex_search('^IMDB_SERVER2') - - __sap_software_download_sap_hana_2_lcapps_list | length > 0 - - __sap_software_download_sap_hana_2_lcapps_list_filtered | length == 0 - - -# Warning conditions: -# - At least one IMDB_AFL100 was found -# - No IMDB_AFL100__ was found -# Examples: -# - IMDB_SERVER100_122_35-10009569.SAR - Maintenance Revision 122.35 (SPS12) for HANA DB 1.00 -# - IMDB_AFL100_122P_3500-10012328.SAR - SAP HANA AFL 1.0 Revision 122.3500 only for HANA DB 122.35 -- name: Relation Check - SAP HANA 1.00 - IMDB_SERVER and IMDB_AFL - ansible.builtin.fail: - msg: | - Warning: Incompatible SAP HANA AFL 1.0 files were found. - Ensure that correct version is selected: {{ __sap_software_download_sap_hana_1_version - }}.{{ __sap_software_download_sap_hana_1_revision }} - - Expected file pattern: IMDB_AFL100_{{ __sap_software_download_sap_hana_1_version }}*_{{ __sap_software_download_sap_hana_1_revision }}* - Actual files detected: {{ __sap_software_download_sap_hana_1_afl_list | unique | join(', ') }} - vars: - __sap_software_download_sap_hana_1_afl_list: - "{{ __sap_software_download_files | select('match', '^IMDB_AFL100.*') | list }}" - __sap_software_download_sap_hana_1_afl_list_filtered: - "{{ sap_software_download_files | select('match', '^IMDB_AFL100_.*' - ~ __sap_software_download_sap_hana_1_version ~ '.*_' ~ __sap_software_download_sap_hana_1_revision) | list }}" - ignore_errors: "{{ sap_software_download_ignore_relation_warning | d(false) }}" - when: - - __sap_software_download_sap_hana | regex_search('^IMDB_SERVER1') - - __sap_software_download_sap_hana_1_afl_list | length > 0 - - __sap_software_download_sap_hana_1_afl_list_filtered | length == 0 - - -# Warning conditions: -# - At least one IMDB_AFL20 was found -# - No IMDB_AFL20_ was found -# Examples: - # - IMDB_SERVER20_084_0-80002031.SAR - Revision 2.00.084.0 (SPS08) for HANA DB 2.0 - # - IMDB_AFL20_084_1-80001894.SAR - SAP HANA AFL Rev 84.1 only for HANA 2.0 Rev 84 -- name: Relation Check - SAP HANA 2.00 - IMDB_SERVER and IMDB_AFL - ansible.builtin.fail: - msg: | - Warning: Incompatible SAP HANA AFL 2.0 files were found. - Ensure that correct version is selected: {{ __sap_software_download_sap_hana_2_version }} - - Expected file pattern: IMDB_AFL20_{{ __sap_software_download_sap_hana_2_version }}* - Actual files detected: {{ __sap_software_download_sap_hana_2_afl_list | unique | join(', ') }} - vars: - __sap_software_download_sap_hana_2_afl_list: - "{{ __sap_software_download_files | select('match', '^IMDB_AFL20.*') | list }}" - __sap_software_download_sap_hana_2_afl_list_filtered: - "{{ sap_software_download_files | select('match', '^IMDB_AFL20_.*' ~ __sap_software_download_sap_hana_2_version) | list }}" - ignore_errors: "{{ sap_software_download_ignore_relation_warning | d(false) }}" - when: - - __sap_software_download_sap_hana | regex_search('^IMDB_SERVER2') - - __sap_software_download_sap_hana_2_afl_list | length > 0 - - __sap_software_download_sap_hana_2_afl_list_filtered | length == 0 - - -# Warning conditions: -# - At least one IMDB_CLIENT was found -# - No IMDB_CLIENT1* was found -# Examples: - # - IMDB_SERVER100_122_35-10009569.SAR - Maintenance Revision 122.35 (SPS12) for HANA DB 1.00 - # - IMDB_CLIENT100_120_140-10009663.SAR - Revision 120 for SAP HANA CLIENT 1.00 -- name: Relation Check - SAP HANA 1.00 - IMDB_SERVER and IMDB_CLIENT - ansible.builtin.fail: - msg: | - Warning: SAP HANA CLIENT 1.00 was not found. - - Expected file pattern: IMDB_CLIENT100_* - Actual files detected: {{ __sap_software_download_sap_hana_client_list | unique | join(', ') }} - vars: - __sap_software_download_sap_hana_client_list: - "{{ __sap_software_download_files | select('match', '^IMDB_CLIENT.*') | list }}" - ignore_errors: "{{ sap_software_download_ignore_relation_warning | d(false) }}" - when: - - __sap_software_download_sap_hana | regex_search('^IMDB_SERVER1') - and __sap_software_download_sap_hana_client_list | length > 0 - and __sap_software_download_sap_hana_client_list | select('match', '^IMDB_CLIENT1.*') | length == 0 - - -# Warning conditions: -# - At least one IMDB_CLIENT was found -# - No IMDB_CLIENT2* was found -# Examples: - # - IMDB_SERVER20_084_0-80002031.SAR - Revision 2.00.084.0 (SPS08) for HANA DB 2.0 - # - IMDB_CLIENT20_024_21-80002082.SAR - SAP HANA CLIENT Version 2.24 -- name: Relation Check - SAP HANA 2.00 - IMDB_SERVER and IMDB_CLIENT - ansible.builtin.fail: - msg: | - Warning: SAP HANA CLIENT 2.00 was not found. - - Expected file pattern: IMDB_CLIENT20_* - Actual files detected: {{ __sap_software_download_sap_hana_client_list | unique | join(', ') }} - vars: - __sap_software_download_sap_hana_client_list: - "{{ __sap_software_download_files | select('match', '^IMDB_CLIENT.*') | list }}" - ignore_errors: "{{ sap_software_download_ignore_relation_warning | d(false) }}" - when: - - __sap_software_download_sap_hana | regex_search('^IMDB_SERVER2') - and __sap_software_download_sap_hana_client_list | length > 0 - and __sap_software_download_sap_hana_client_list | select('match', '^IMDB_CLIENT2.*') | length == 0 diff --git a/roles/sap_software_download/tasks/pre_steps/06_validate_sap_hana_1.yml b/roles/sap_software_download/tasks/pre_steps/06_validate_sap_hana_1.yml new file mode 100644 index 0000000..dbe9e82 --- /dev/null +++ b/roles/sap_software_download/tasks/pre_steps/06_validate_sap_hana_1.yml @@ -0,0 +1,87 @@ +# SPDX-License-Identifier: Apache-2.0 +--- + +- name: Relationship Validation - SAP HANA 1.00 - Set HANA variables + ansible.builtin.set_fact: + # 122 for IMDB_SERVER100_122_35-10009569.SAR + __sap_software_download_sap_hana_1_version: + "{{ __sap_software_download_sap_hana.split('_')[2] | regex_replace('[^0-9]', '') }}" + + # 35 for IMDB_SERVER100_122_35-10009569.SAR + __sap_software_download_sap_hana_1_revision: + "{{ __sap_software_download_sap_hana.split('_')[3].split('-')[0] | regex_replace('[^0-9]', '') }}" + + +# Warning conditions: +# - At least one IMDB_LCAPPS_1 was found +# - No IMDB_LCAPPS__ was found +# Examples: +# - IMDB_SERVER100_122_35-10009569.SAR - Maintenance Revision 122.35 (SPS12) for HANA DB 1.00 +# - IMDB_LCAPPS_122P_3500-20010426.SAR - LCAPPS for HANA 1.00.122.35 Build 100.47 PL 017 +- name: Relationship Validation - SAP HANA 1.00 - IMDB_SERVER and IMDB_LCAPPS + ansible.builtin.fail: + msg: | + Warning: Incompatible SAP HANA component LCAPPS files were found for detected SAP HANA DATABASE 1.0 {{ + __sap_software_download_sap_hana_1_version }}.{{ __sap_software_download_sap_hana_1_revision }}. + + Expected file pattern: IMDB_LCAPPS_{{ + __sap_software_download_sap_hana_1_version }}*_{{__sap_software_download_sap_hana_1_revision }}* + Actual files detected: {{ __sap_software_download_sap_hana_1_lcapps_list | unique | join(', ') }} + vars: + __sap_software_download_sap_hana_1_lcapps_list: + "{{ __sap_software_download_files | select('match', '^IMDB_LCAPPS_1.*') | list }}" + __sap_software_download_sap_hana_1_lcapps_list_filtered: + "{{ sap_software_download_files | select('match', '^IMDB_LCAPPS_.*' + ~ __sap_software_download_sap_hana_1_version ~ '.*_' ~ __sap_software_download_sap_hana_1_revision) | list }}" + ignore_errors: "{{ sap_software_download_ignore_relationship_warning | d(false) }}" + when: + - __sap_software_download_sap_hana_1_lcapps_list | length > 0 + - __sap_software_download_sap_hana_1_lcapps_list_filtered | length == 0 + + +# Warning conditions: +# - At least one IMDB_AFL100 was found +# - No IMDB_AFL100__ was found +# Examples: +# - IMDB_SERVER100_122_35-10009569.SAR - Maintenance Revision 122.35 (SPS12) for HANA DB 1.00 +# - IMDB_AFL100_122P_3500-10012328.SAR - SAP HANA AFL 1.0 Revision 122.3500 only for HANA DB 122.35 +- name: Relationship Validation - SAP HANA 1.00 - IMDB_SERVER and IMDB_AFL + ansible.builtin.fail: + msg: | + Warning: Incompatible SAP HANA component AFL files were found for detected SAP HANA DATABASE 1.0 {{ + __sap_software_download_sap_hana_1_version }}.{{ __sap_software_download_sap_hana_1_revision }}. + + Expected file pattern: IMDB_AFL100_{{ __sap_software_download_sap_hana_1_version }}*_{{ __sap_software_download_sap_hana_1_revision }}* + Actual files detected: {{ __sap_software_download_sap_hana_1_afl_list | unique | join(', ') }} + vars: + __sap_software_download_sap_hana_1_afl_list: + "{{ __sap_software_download_files | select('match', '^IMDB_AFL100.*') | list }}" + __sap_software_download_sap_hana_1_afl_list_filtered: + "{{ sap_software_download_files | select('match', '^IMDB_AFL100_.*' + ~ __sap_software_download_sap_hana_1_version ~ '.*_' ~ __sap_software_download_sap_hana_1_revision) | list }}" + ignore_errors: "{{ sap_software_download_ignore_relationship_warning | d(false) }}" + when: + - __sap_software_download_sap_hana_1_afl_list | length > 0 + - __sap_software_download_sap_hana_1_afl_list_filtered | length == 0 + + +# Warning conditions: +# - At least one IMDB_CLIENT was found +# - No IMDB_CLIENT1* was found +# Examples: + # - IMDB_SERVER100_122_35-10009569.SAR - Maintenance Revision 122.35 (SPS12) for HANA DB 1.00 + # - IMDB_CLIENT100_120_140-10009663.SAR - Revision 120 for SAP HANA CLIENT 1.00 +- name: Relationship Validation - SAP HANA 1.00 - IMDB_SERVER and IMDB_CLIENT + ansible.builtin.fail: + msg: | + Warning: Incompatible SAP HANA client files were found for detected SAP HANA DATABASE 1.0. + + Expected file pattern: IMDB_CLIENT100_* + Actual files detected: {{ __sap_software_download_sap_hana_client_list | unique | join(', ') }} + vars: + __sap_software_download_sap_hana_client_list: + "{{ __sap_software_download_files | select('match', '^IMDB_CLIENT.*') | list }}" + ignore_errors: "{{ sap_software_download_ignore_relationship_warning | d(false) }}" + when: + - __sap_software_download_sap_hana_client_list | length > 0 + - __sap_software_download_sap_hana_client_list | select('match', '^IMDB_CLIENT1.*') | length == 0 diff --git a/roles/sap_software_download/tasks/pre_steps/07_validate_sap_hana_2.yml b/roles/sap_software_download/tasks/pre_steps/07_validate_sap_hana_2.yml new file mode 100644 index 0000000..3bbb21b --- /dev/null +++ b/roles/sap_software_download/tasks/pre_steps/07_validate_sap_hana_2.yml @@ -0,0 +1,80 @@ +# SPDX-License-Identifier: Apache-2.0 +--- + +- name: Relationship Validation - SAP HANA 2.00 - Set HANA variables + ansible.builtin.set_fact: + # 084 for IMDB_SERVER20_084_0-80002031.SAR + __sap_software_download_sap_hana_2_version: + "{{ __sap_software_download_sap_hana.split('_')[2] }}" + + +# Warning conditions: +# - At least one IMDB_LCAPPS_2 was found +# - No IMDB_LCAPPS_2 was found +# Examples: + # - IMDB_SERVER20_084_0-80002031.SAR - Revision 2.00.084.0 (SPS08) for HANA DB 2.0 + # - IMDB_LCAPPS_2084_0-20010426.SAR - LCAPPS for HANA 2.0 Rev 84 Build 101.19 PL 007 +- name: Relationship Validation - SAP HANA 2.00 - IMDB_SERVER and IMDB_LCAPPS + ansible.builtin.fail: + msg: | + Warning: Incompatible SAP HANA component LCAPPS files were found for detected SAP HANA DATABASE 2.0 Revision {{ + __sap_software_download_sap_hana_2_version }}. + + Expected file pattern: IMDB_LCAPPS_2{{ __sap_software_download_sap_hana_2_version }}* + Actual files detected: {{ __sap_software_download_sap_hana_2_lcapps_list | unique | join(', ') }} + vars: + __sap_software_download_sap_hana_2_lcapps_list: + "{{ __sap_software_download_files | select('match', '^IMDB_LCAPPS_2.*') | list }}" + __sap_software_download_sap_hana_2_lcapps_list_filtered: + "{{ sap_software_download_files | select('match', '^IMDB_LCAPPS_2' ~ __sap_software_download_sap_hana_2_version) | list }}" + ignore_errors: "{{ sap_software_download_ignore_relationship_warning | d(false) }}" + when: + - __sap_software_download_sap_hana_2_lcapps_list | length > 0 + - __sap_software_download_sap_hana_2_lcapps_list_filtered | length == 0 + + +# Warning conditions: +# - At least one IMDB_AFL20 was found +# - No IMDB_AFL20_ was found +# Examples: + # - IMDB_SERVER20_084_0-80002031.SAR - Revision 2.00.084.0 (SPS08) for HANA DB 2.0 + # - IMDB_AFL20_084_1-80001894.SAR - SAP HANA AFL Rev 84.1 only for HANA 2.0 Rev 84 +- name: Relationship Validation - SAP HANA 2.00 - IMDB_SERVER and IMDB_AFL + ansible.builtin.fail: + msg: | + Warning: Incompatible SAP HANA component AFL files were found for detected SAP HANA DATABASE 2.0 Revision {{ + __sap_software_download_sap_hana_2_version }}. + + Expected file pattern: IMDB_AFL20_{{ __sap_software_download_sap_hana_2_version }}* + Actual files detected: {{ __sap_software_download_sap_hana_2_afl_list | unique | join(', ') }} + vars: + __sap_software_download_sap_hana_2_afl_list: + "{{ __sap_software_download_files | select('match', '^IMDB_AFL20.*') | list }}" + __sap_software_download_sap_hana_2_afl_list_filtered: + "{{ sap_software_download_files | select('match', '^IMDB_AFL20_.*' ~ __sap_software_download_sap_hana_2_version) | list }}" + ignore_errors: "{{ sap_software_download_ignore_relationship_warning | d(false) }}" + when: + - __sap_software_download_sap_hana_2_afl_list | length > 0 + - __sap_software_download_sap_hana_2_afl_list_filtered | length == 0 + + +# Warning conditions: +# - At least one IMDB_CLIENT was found +# - No IMDB_CLIENT2* was found +# Examples: + # - IMDB_SERVER20_084_0-80002031.SAR - Revision 2.00.084.0 (SPS08) for HANA DB 2.0 + # - IMDB_CLIENT20_024_21-80002082.SAR - SAP HANA CLIENT Version 2.24 +- name: Relationship Validation - SAP HANA 2.00 - IMDB_SERVER and IMDB_CLIENT + ansible.builtin.fail: + msg: | + Warning: Incompatible SAP HANA client files were found for detected SAP HANA DATABASE 2.0. + + Expected file pattern: IMDB_CLIENT20_* + Actual files detected: {{ __sap_software_download_sap_hana_client_list | unique | join(', ') }} + vars: + __sap_software_download_sap_hana_client_list: + "{{ __sap_software_download_files | select('match', '^IMDB_CLIENT.*') | list }}" + ignore_errors: "{{ sap_software_download_ignore_relationship_warning | d(false) }}" + when: + - __sap_software_download_sap_hana_client_list | length > 0 + - __sap_software_download_sap_hana_client_list | select('match', '^IMDB_CLIENT2.*') | length == 0 From ef1204df4fdf8413ddd83f206f51f227ec993308 Mon Sep 17 00:00:00 2001 From: sean-freeman <1815807+sean-freeman@users.noreply.github.com> Date: Mon, 31 Mar 2025 13:07:35 +0100 Subject: [PATCH 13/26] fix: cache gigya sdk build number --- plugins/module_utils/sap_id_sso.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/module_utils/sap_id_sso.py b/plugins/module_utils/sap_id_sso.py index c0028d2..1078d56 100644 --- a/plugins/module_utils/sap_id_sso.py +++ b/plugins/module_utils/sap_id_sso.py @@ -215,6 +215,7 @@ def _get_sdk_build_number(api_key): build_number = match.group(1) logger.debug(f'gigya sdk build number: {build_number}') + GIGYA_SDK_BUILD_NUMBER = build_number return build_number From 024f0836f8d3c8e0fce8c5396240bf8c342638bc Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Tue, 1 Apr 2025 13:56:39 +0200 Subject: [PATCH 14/26] feat: response error handling, SPS version, verbosity, plan debug --- plugins/module_utils/sap_api_common.py | 20 ++++++++++++++++--- roles/sap_software_download/meta/main.yml | 20 +++++++++++++++++++ .../tasks/download_plan.yml | 2 +- .../pre_steps/03_validate_credentials.yml | 8 ++++++-- .../pre_steps/06_validate_sap_hana_1.yml | 12 ++++++----- .../pre_steps/07_validate_sap_hana_2.yml | 8 ++++---- 6 files changed, 55 insertions(+), 15 deletions(-) create mode 100644 roles/sap_software_download/meta/main.yml diff --git a/plugins/module_utils/sap_api_common.py b/plugins/module_utils/sap_api_common.py index 204b492..164b173 100644 --- a/plugins/module_utils/sap_api_common.py +++ b/plugins/module_utils/sap_api_common.py @@ -37,9 +37,23 @@ def _request(url, **kwargs): method = 'POST' if kwargs.get('data') or kwargs.get('json') else 'GET' res = https_session.request(method, url, **kwargs) - if (res.status_code == 403 - and res.json()['errorMessage'].startswith('Account Temporarily Locked Out')): - raise Exception('SAP ID Service has reported `Account Temporarily Locked Out`. Please reset password to regain access and try again.') + # Validating against `res.text` can cause long execution time, because fuzzy search result can contain large `res.text`. + # This can be prevented by validating `res.status_code` check before `res.text`. + # Example: 'Two-Factor Authentication' is only in `res.text`, which can lead to long execution. + + if res.status_code == 403: + if 'You are not authorized to download this file' in res.text: + raise Exception(f'You are not authorized to download this file.') + elif 'Account Temporarily Locked Out' in res.text: + raise Exception(f'Account Temporarily Locked Out. Please reset password to regain access and try again.') + else: + res.raise_for_status() + + if res.status_code == 404: + if 'The file you have requested cannot be found' in res.text: + raise Exception(f'The file you have requested cannot be found.') + else: + res.raise_for_status() res.raise_for_status() diff --git a/roles/sap_software_download/meta/main.yml b/roles/sap_software_download/meta/main.yml new file mode 100644 index 0000000..cdd1066 --- /dev/null +++ b/roles/sap_software_download/meta/main.yml @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: Apache-2.0 +--- +galaxy_info: + namespace: community + author: SUSE, Marcel Mamula + description: Downloads SAP Software Media from SAP using an S-User ID and password, supporting both direct file downloads and Maintenance Plan transactions. + company: SUSE + license: Apache-2.0 + min_ansible_version: 2.16 + platforms: + - name: EL + versions: ["7", "8", "9"] + - name: SLES + versions: ["15", "16"] + galaxy_tags: + - sap + - download + - suse + - redhat +dependencies: [] diff --git a/roles/sap_software_download/tasks/download_plan.yml b/roles/sap_software_download/tasks/download_plan.yml index 34b4d31..ecd8565 100644 --- a/roles/sap_software_download/tasks/download_plan.yml +++ b/roles/sap_software_download/tasks/download_plan.yml @@ -51,7 +51,7 @@ - name: Download - Maintenance Plan - Show failed results ansible.builtin.fail: msg: | - Maintenance Plan file(s) not found: {{ __failed_items | map(attribute='Filename') | list | join(', ') }} + Maintenance Plan file(s) not found: {{ __failed_items | map(attribute='item.Filename') | list | join(', ') }} Verify your Maintenance Plan on SAP Launchpad before retrying. vars: __failed_items: "{{ __sap_software_download_files_plan_results.results diff --git a/roles/sap_software_download/tasks/pre_steps/03_validate_credentials.yml b/roles/sap_software_download/tasks/pre_steps/03_validate_credentials.yml index ef63819..51a9801 100644 --- a/roles/sap_software_download/tasks/pre_steps/03_validate_credentials.yml +++ b/roles/sap_software_download/tasks/pre_steps/03_validate_credentials.yml @@ -2,7 +2,7 @@ --- # This task attempts dry run to check SAPCAR file in order to validate provided credentials. -- name: Validate Credentials - Dry run to download test file with Python venv +- name: Validate Credentials - Dry run check user credentials and download privilege with Python venv when: sap_software_download_use_venv | d(true) community.sap_launchpad.software_center_download: suser_id: "{{ sap_software_download_suser_id }}" @@ -13,6 +13,8 @@ deduplicate: "last" dry_run: true register: __sap_software_download_validate_credentials_venv + retries: 1 + delay: 5 environment: PATH: "{{ __sap_software_download_venv.path }}/bin:{{ ansible_env.PATH }}" PYTHONPATH: "{{ __sap_software_download_venv.path }}/lib/{{ sap_software_download_python_interpreter }}/site-packages" @@ -21,7 +23,7 @@ ansible_python_interpreter: "{{ __sap_software_download_venv.path ~ '/bin/' ~ sap_software_download_python_interpreter }}" ignore_errors: true # Errors are ignored and validated afterwards -- name: Validate Credentials - Dry run to download test file with Python system default +- name: Validate Credentials - Dry run check user credentials and download privilege with Python system default when: not sap_software_download_use_venv | d(true) community.sap_launchpad.software_center_download: suser_id: "{{ sap_software_download_suser_id }}" @@ -32,6 +34,8 @@ deduplicate: "last" dry_run: true register: __sap_software_download_validate_credentials_default + retries: 1 + delay: 5 vars: ansible_python_interpreter: "{{ '/usr/bin/' ~ sap_software_download_python_interpreter }}" ignore_errors: true # Errors are ignored and validated afterwards diff --git a/roles/sap_software_download/tasks/pre_steps/06_validate_sap_hana_1.yml b/roles/sap_software_download/tasks/pre_steps/06_validate_sap_hana_1.yml index dbe9e82..9a72e54 100644 --- a/roles/sap_software_download/tasks/pre_steps/06_validate_sap_hana_1.yml +++ b/roles/sap_software_download/tasks/pre_steps/06_validate_sap_hana_1.yml @@ -6,7 +6,7 @@ # 122 for IMDB_SERVER100_122_35-10009569.SAR __sap_software_download_sap_hana_1_version: "{{ __sap_software_download_sap_hana.split('_')[2] | regex_replace('[^0-9]', '') }}" - + # 35 for IMDB_SERVER100_122_35-10009569.SAR __sap_software_download_sap_hana_1_revision: "{{ __sap_software_download_sap_hana.split('_')[3].split('-')[0] | regex_replace('[^0-9]', '') }}" @@ -21,8 +21,9 @@ - name: Relationship Validation - SAP HANA 1.00 - IMDB_SERVER and IMDB_LCAPPS ansible.builtin.fail: msg: | - Warning: Incompatible SAP HANA component LCAPPS files were found for detected SAP HANA DATABASE 1.0 {{ - __sap_software_download_sap_hana_1_version }}.{{ __sap_software_download_sap_hana_1_revision }}. + Warning: Incompatible SAP HANA component LCAPPS files were found for detected SAP HANA DATABASE 1.0 SPS {{ + __sap_software_download_sap_hana_1_version[:2] }} Revision {{ __sap_software_download_sap_hana_1_version + }}.{{ __sap_software_download_sap_hana_1_revision }}. Expected file pattern: IMDB_LCAPPS_{{ __sap_software_download_sap_hana_1_version }}*_{{__sap_software_download_sap_hana_1_revision }}* @@ -48,8 +49,9 @@ - name: Relationship Validation - SAP HANA 1.00 - IMDB_SERVER and IMDB_AFL ansible.builtin.fail: msg: | - Warning: Incompatible SAP HANA component AFL files were found for detected SAP HANA DATABASE 1.0 {{ - __sap_software_download_sap_hana_1_version }}.{{ __sap_software_download_sap_hana_1_revision }}. + Warning: Incompatible SAP HANA component AFL files were found for detected SAP HANA DATABASE 1.0 SPS {{ + __sap_software_download_sap_hana_1_version[:2] }} Revision {{ __sap_software_download_sap_hana_1_version + }}.{{ __sap_software_download_sap_hana_1_revision }}. Expected file pattern: IMDB_AFL100_{{ __sap_software_download_sap_hana_1_version }}*_{{ __sap_software_download_sap_hana_1_revision }}* Actual files detected: {{ __sap_software_download_sap_hana_1_afl_list | unique | join(', ') }} diff --git a/roles/sap_software_download/tasks/pre_steps/07_validate_sap_hana_2.yml b/roles/sap_software_download/tasks/pre_steps/07_validate_sap_hana_2.yml index 3bbb21b..094fca7 100644 --- a/roles/sap_software_download/tasks/pre_steps/07_validate_sap_hana_2.yml +++ b/roles/sap_software_download/tasks/pre_steps/07_validate_sap_hana_2.yml @@ -17,8 +17,8 @@ - name: Relationship Validation - SAP HANA 2.00 - IMDB_SERVER and IMDB_LCAPPS ansible.builtin.fail: msg: | - Warning: Incompatible SAP HANA component LCAPPS files were found for detected SAP HANA DATABASE 2.0 Revision {{ - __sap_software_download_sap_hana_2_version }}. + Warning: Incompatible SAP HANA component LCAPPS files were found for detected SAP HANA DATABASE 2.0 SPS {{ + __sap_software_download_sap_hana_2_version[:2] }} Revision {{ __sap_software_download_sap_hana_2_version }}. Expected file pattern: IMDB_LCAPPS_2{{ __sap_software_download_sap_hana_2_version }}* Actual files detected: {{ __sap_software_download_sap_hana_2_lcapps_list | unique | join(', ') }} @@ -42,8 +42,8 @@ - name: Relationship Validation - SAP HANA 2.00 - IMDB_SERVER and IMDB_AFL ansible.builtin.fail: msg: | - Warning: Incompatible SAP HANA component AFL files were found for detected SAP HANA DATABASE 2.0 Revision {{ - __sap_software_download_sap_hana_2_version }}. + Warning: Incompatible SAP HANA component AFL files were found for detected SAP HANA DATABASE 2.0 SPS {{ + __sap_software_download_sap_hana_2_version[:2] }} Revision {{ __sap_software_download_sap_hana_2_version }}. Expected file pattern: IMDB_AFL20_{{ __sap_software_download_sap_hana_2_version }}* Actual files detected: {{ __sap_software_download_sap_hana_2_afl_list | unique | join(', ') }} From 2631c71794f01d9322cacf883baa60343f3d7332 Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Tue, 1 Apr 2025 15:59:58 +0200 Subject: [PATCH 15/26] feat: python default module packages --- roles/sap_software_download/README.md | 103 ++++++++++++++++-- roles/sap_software_download/defaults/main.yml | 9 ++ .../meta/argument_spec.yml | 14 ++- .../tasks/pre_steps/01_include_variables.yml | 12 +- .../02_prepare_python_environment.yml | 9 +- roles/sap_software_download/vars/RedHat.yml | 9 ++ .../sap_software_download/vars/RedHat_10.yml | 9 ++ roles/sap_software_download/vars/Suse.yml | 21 ++++ roles/sap_software_download/vars/main.yml | 10 ++ 9 files changed, 183 insertions(+), 13 deletions(-) diff --git a/roles/sap_software_download/README.md b/roles/sap_software_download/README.md index a85a387..93fdd7b 100644 --- a/roles/sap_software_download/README.md +++ b/roles/sap_software_download/README.md @@ -8,6 +8,22 @@ The Ansible Role `sap_software_download` is used to download SAP Software Media +## Dependencies +This role requires the following Python modules to be installed on the target node (the machine where SAP software will be downloaded): + +- wheel +- urllib3 +- requests +- beautifulsoup4 +- lxml + +The role installs these modules if they are not already present. Installation can be done in one of the following ways: + +- **Virtual Environment (Default):** + - When `sap_software_download_use_venv` is `true` (default), a temporary virtual environment is created, and modules are installed via `pip`. + - The Python version is determined by `sap_software_download_python_interpreter`. +- **System Environment:** + - When `sap_software_download_use_venv` is `false`, modules are installed directly into the system's Python environment using OS packages specified by `sap_software_download_python_module_packages`. @@ -34,13 +50,14 @@ The target node must meet the following requirements: 2. **Python Environment Preparation:** The role prepares the Python environment: * **Virtual Environment (Default):** A temporary Python virtual environment is created, and all necessary dependencies are installed within it. * **System Environment:** Alternatively, if `sap_software_download_use_venv` is set to `false`, dependencies are installed directly into the system's default Python environment. -3. **Maintenance Plan File List:** If the `sap_software_download_mp_transaction` variable is provided, the role retrieves the list of files associated with the specified Maintenance Plan transaction. -4. **File Relationship Validation:** If `sap_software_download_validate_relationships` is `true`, the role performs validation checks on the relationships between the files to be downloaded. +3. **Validate provided S-User credentials** The role will search for `SAPCAR` file to validate credentials and download authorization. +4. **Maintenance Plan File List:** If the `sap_software_download_mp_transaction` variable is provided, the role retrieves the list of files associated with the specified Maintenance Plan transaction. +5. **File Relationship Validation:** If `sap_software_download_validate_relationships` is `true`, the role performs validation checks on the relationships between the files to be downloaded. * **Alternative File Search:** If `sap_software_download_find_alternatives` is `true`, the role will search for alternative files if the requested files are not found. * More information about validation logic is available at [Explanation of relationship validation logic](#explanation-of-relationship-validation-logic) -5. **Maintenance Plan File Download:** If `sap_software_download_mp_transaction` is provided, the role downloads the files associated with the Maintenance Plan. -6. **Direct File Download:** If `sap_software_download_files` is provided, the role downloads the specified files. -7. **Virtual Environment Cleanup:** If a temporary Python virtual environment was used, it is removed. +6. **Maintenance Plan File Download:** If `sap_software_download_mp_transaction` is provided, the role downloads the files associated with the Maintenance Plan. +7. **Direct File Download:** If `sap_software_download_files` is provided, the role downloads the specified files. +8. **Virtual Environment Cleanup:** If a temporary Python virtual environment was used, it is removed. @@ -81,6 +98,62 @@ Download of SAP Software files using Maintenance Plan sap_software_download_directory: "/software" sap_software_download_mp_transaction: 'Transaction Name or Display ID from Maintenance Planner' ``` + +Combined download of SAP Software files and Maintenance Plan transaction together with settings: +- Use default Python instead of Python virtual environment +- No validation of S-User credentials +- No validation of relationships +- No warnings for unavailable files +- No warnings for unavailable Maintenance Plan transaction +```yaml +- name: Ansible Play for downloading SAP Software + hosts: localhost + become: true + tasks: + - name: Include role sap_software_download + ansible.builtin.include_role: + name: community.sap_launchpad.sap_software_download + vars: + sap_software_download_suser_id: "Enter SAP S-User ID" + sap_software_download_suser_password: "Enter SAP S-User Password" + sap_software_download_directory: "/software" + sap_software_download_use_venv: false + sap_software_download_ignore_validate_credentials: true + sap_software_download_ignore_file_not_found: true + sap_software_download_ignore_plan_not_found: true + sap_software_download_validate_relationships: false + sap_software_download_deduplicate: first + sap_software_download_files: + - 'SAPCAR_1115-70006178.EXE' + - 'SAPEXE_100-80005509.SAR' + sap_software_download_mp_transaction: 'Transaction Name or Display ID from Maintenance Planner' +``` +Download of SAP Software files using Python version `3.13`. +```yaml +--- +- name: Ansible Play for downloading SAP Software + hosts: localhost + become: true + tasks: + - name: Include role sap_software_download + ansible.builtin.include_role: + name: community.sap_launchpad.sap_software_download + vars: + sap_software_download_python_interpreter: python3.13 + sap_software_download_python_package: python313 + sap_software_download_python_module_packages: + - python313-wheel + - python313-urllib3 + - python313-requests + - python313-beautifulsoup4 + - python313-lxml + sap_software_download_suser_id: "Enter SAP S-User ID" + sap_software_download_suser_password: "Enter SAP S-User Password" + sap_software_download_directory: "/software" + sap_software_download_files: + - 'SAPCAR_1115-70006178.EXE' + - 'SAPEXE_100-80005509.SAR' +``` @@ -93,20 +166,20 @@ Validation is executed for known combinations of files, where we can validate th Example for SAP HANA Database Server 2.0 with LCAPPS and AFL.
1. All files are examined, and a file starting with `IMDB_SERVER2` is found: `IMDB_SERVER20_084_0-80002031.SAR (Revision 2.00.084.0 (SPS08))`. This indicates a SAP HANA 2.0 database server file. -2. The HANA version and revision are extracted from the file name: `HANA 2.0`, `Revision 084`. +2. The HANA version and revision are extracted from the file name: `HANA 2.0`, `Version 084`. 3. Validation for HANA 1.0 is skipped because it expects files starting with `IMDB_SERVER1`. The following steps are only for `IMDB_SERVER2` (HANA 2.0). 4. All files are examined for files starting with `IMDB_LCAPPS_2` (indicating LCAPPS for HANA 2.0). Then the list is filtered to only include files starting with `IMDB_LCAPPS_2084` (indicating LCAPPS for HANA 2.0 revision 084). 5. Validation will have two outcomes: - A file like `IMDB_LCAPPS_2084_0-20010426.SAR` is present. In this case, validation will pass because the LCAPPS version is compatible with the HANA revision. - No file starting with `IMDB_LCAPPS_2084` is present, but there are files starting with `IMDB_LCAPPS_2`.
This indicates a mismatch because the LCAPPS version is not compatible with the specific HANA revision (084) found in step 2.
- In this case, validation will fail. This can be prevented by setting `sap_software_download_ignore_relationship_warning` to `true`. + In this case, validation will fail. This can be ignored by setting `sap_software_download_ignore_relationship_warning` to `true`. 6. All files are examined for files starting with `IMDB_AFL20` (indicating AFL for HANA 2.0). Then the list is filtered to only include files starting with `IMDB_AFL20_084` (indicating AFL for HANA 2.0 revision 084). 7. Validation will have two outcomes: - A file like `IMDB_AFL20_084_1-80001894.SAR` is present. In this case, validation will pass because the AFL version is compatible with the HANA revision. - No file starting with `IMDB_AFL20_084` is present, but there are files starting with `IMDB_AFL20`.
This indicates a mismatch because the AFL version is not compatible with the specific HANA revision (084) found in step 2.
- In this case, validation will fail. This can be prevented by setting `sap_software_download_ignore_relationship_warning` to `true`. + In this case, validation will fail. This can be ignored by setting `sap_software_download_ignore_relationship_warning` to `true`. This validation example checks major and minor release (SPS and Revision), but it does not validate patch version. @@ -125,6 +198,7 @@ Apache 2.0 ### sap_software_download_python_interpreter - _Type:_ `string`
+- _Default:_ **Determined by the operating system.**
The Python interpreter executable to use when creating a Python virtual environment.
**Mandatory** when `sap_software_download_use_venv` is `true`.
@@ -134,12 +208,25 @@ Examples: `python3.11` (SUSE), `python3.9` (Red Hat)
### sap_software_download_python_package - _Type:_ `string`
+- _Default:_ **Determined by the operating system.**
The name of the OS package that provides the desired Python version.
The Python version provided by this package must match the version specified by `sap_software_download_python_interpreter`.
The default value is determined by the operating system and is set in the corresponding OS-specific variables file.
Examples: `python311` (SUSE), `python3.9` (Red Hat)
+### sap_software_download_python_module_packages +- _Type:_ `list` with elements of type `string`
+- _Default:_ **Determined by the operating system.**
+ +The list of the OS packages that provide modules for the desired Python version.
+Required modules are wheel, urllib3, requests, beautifulsoup4, lxml
+The listed package versions must match the Python version specified by `sap_software_download_python_interpreter`.
+The default value is determined by the operating system and is set in the corresponding OS-specific variables file.
+Examples:
+- `['python311-wheel', 'python311-urllib3', 'python311-requests', 'python311-beautifulsoup4', 'python311-lxml']` (SUSE)
+- `['python3.9-wheel', 'python3.9-urllib3', 'python3.9-requests', 'python3.9-beautifulsoup4', 'python3.9-lxml']` (Red Hat)
+ ### sap_software_download_use_venv - _Type:_ `boolean`
- _Default:_ `true`
diff --git a/roles/sap_software_download/defaults/main.yml b/roles/sap_software_download/defaults/main.yml index 13787f9..cc2fcab 100644 --- a/roles/sap_software_download/defaults/main.yml +++ b/roles/sap_software_download/defaults/main.yml @@ -16,6 +16,15 @@ sap_software_download_python_interpreter: sap_software_download_python_package: "{{ __sap_software_download_python_package }}" +# The list of the OS packages that provide modules for the desired Python version. +# Required modules: wheel, urllib3, requests, beautifulsoup4, lxml +# The listed package versions must match the Python version specified by `sap_software_download_python_interpreter`. +# The default value is determined by the operating system and is set in the corresponding OS-specific variables file. Examples: +# python311-wheel, python311-urllib3, python311-requests, python311-beautifulsoup4, python311-lxml (SUSE) +# python3.9-wheel, python3.9-urllib3, python3.9-requests, python3.9-beautifulsoup4, python3.9-lxml (Red Hat) +sap_software_download_python_module_packages: + "{{ __sap_software_download_python_module_packages }}" + # Determines whether to execute the role within a Python virtual environment. # Using a virtual environment is strongly recommended to isolate dependencies. # If set to `false`, the role will install Python dependencies directly into the system's Python environment. diff --git a/roles/sap_software_download/meta/argument_spec.yml b/roles/sap_software_download/meta/argument_spec.yml index dec55ce..adfb6c9 100644 --- a/roles/sap_software_download/meta/argument_spec.yml +++ b/roles/sap_software_download/meta/argument_spec.yml @@ -25,13 +25,25 @@ argument_specs: sap_software_download_python_package: type: str - required: true + required: false description: - The name of the OS package that provides the desired Python version. - The Python version provided by this package must match the version specified by `sap_software_download_python_interpreter`. - The default value is determined by the operating system and is set in the corresponding OS-specific variables file. - Examples are `python311` (SUSE), `python3.9` (Red Hat) + sap_software_download_python_module_packages: + type: list + elements: str + required: false + description: + - The list of the OS packages that provide modules for the desired Python version. + - Required modules are wheel, urllib3, requests, beautifulsoup4, lxml + - The listed package versions must match the Python version specified by `sap_software_download_python_interpreter`. + - The default value is determined by the operating system and is set in the corresponding OS-specific variables file. Examples are + - python311-wheel, python311-urllib3, python311-requests, python311-beautifulsoup4, python311-lxml (SUSE) + - python3.9-wheel, python3.9-urllib3, python3.9-requests, python3.9-beautifulsoup4, python3.9-lxml (Red Hat) + sap_software_download_use_venv: type: bool required: false diff --git a/roles/sap_software_download/tasks/pre_steps/01_include_variables.yml b/roles/sap_software_download/tasks/pre_steps/01_include_variables.yml index f8611db..2eeac53 100644 --- a/roles/sap_software_download/tasks/pre_steps/01_include_variables.yml +++ b/roles/sap_software_download/tasks/pre_steps/01_include_variables.yml @@ -34,7 +34,7 @@ - name: Pre-Steps - Assert that SAP Software files were specified ansible.builtin.assert: that: - - (sap_software_download_files is defined and sap_software_download_files | length > 0) + - (sap_software_download_files is defined and sap_software_download_files | type_debug == 'list' and sap_software_download_files | length > 0) or (sap_software_download_mp_transaction is defined and sap_software_download_mp_transaction | length > 0) fail_msg: | Neither `sap_software_download_files` or `sap_software_download_mp_transaction` are valid. @@ -76,6 +76,16 @@ Empty variable `sap_software_download_python_package`. Ensure that it contains correct Python package name or revert back to defaults. +- name: "Pre-Steps - Verify variable: sap_software_download_python_module_packages" + ansible.builtin.assert: + that: + - sap_software_download_python_module_packages is defined + - sap_software_download_python_module_packages | type_debug == 'list' + - sap_software_download_python_module_packages | length > 0 + fail_msg: | + Empty variable `sap_software_download_python_module_packages`. + Ensure that it contains correct list of Python module package names or revert back to defaults. + - name: "Pre-Steps - Verify variable: sap_software_download_suser_id" ansible.builtin.assert: that: diff --git a/roles/sap_software_download/tasks/pre_steps/02_prepare_python_environment.yml b/roles/sap_software_download/tasks/pre_steps/02_prepare_python_environment.yml index bc06da5..2be5480 100644 --- a/roles/sap_software_download/tasks/pre_steps/02_prepare_python_environment.yml +++ b/roles/sap_software_download/tasks/pre_steps/02_prepare_python_environment.yml @@ -35,6 +35,9 @@ - name: Pre-Steps - Block for default Python preparation when: not sap_software_download_use_venv | d(true) block: - - name: Pre-Steps - Install Python modules in default Python - ansible.builtin.pip: - name: "{{ __sap_software_download_python_modules }}" + # Packages with python modules are installed instead of modules to avoid error: + # `externally-managed-environment` which requires `--break-system-packages` + - name: Pre-Steps - Install Python module packages + ansible.builtin.package: + name: "{{ __sap_software_download_python_module_packages }}" + state: present diff --git a/roles/sap_software_download/vars/RedHat.yml b/roles/sap_software_download/vars/RedHat.yml index 610fea6..6f8dc21 100644 --- a/roles/sap_software_download/vars/RedHat.yml +++ b/roles/sap_software_download/vars/RedHat.yml @@ -11,3 +11,12 @@ __sap_software_download_python_interpreter: 'python3.9' # Set which Python package will be installed on destination node. __sap_software_download_python_package: 'python3.9' + +# The list of required Python Modules in packages +# This is required in order to avoid externally-managed-environment error. +__sap_software_download_python_module_packages: + - python3.9-wheel + - python3.9-urllib3 + - python3.9-requests + - python3.9-beautifulsoup4 + - python3.9-lxml diff --git a/roles/sap_software_download/vars/RedHat_10.yml b/roles/sap_software_download/vars/RedHat_10.yml index 122cc07..13cb27b 100644 --- a/roles/sap_software_download/vars/RedHat_10.yml +++ b/roles/sap_software_download/vars/RedHat_10.yml @@ -10,3 +10,12 @@ __sap_software_download_python_interpreter: 'python3.12' # Set which Python package will be installed on destination node. __sap_software_download_python_package: 'python3.12' + +# The list of required Python Modules in packages +# This is required in order to avoid externally-managed-environment error. +__sap_software_download_python_module_packages: + - python3.12-wheel + - python3.12-urllib3 + - python3.12-requests + - python3.12-beautifulsoup4 + - python3.12-lxml diff --git a/roles/sap_software_download/vars/Suse.yml b/roles/sap_software_download/vars/Suse.yml index 2cd68f3..f540820 100644 --- a/roles/sap_software_download/vars/Suse.yml +++ b/roles/sap_software_download/vars/Suse.yml @@ -23,3 +23,24 @@ __sap_software_download_python_package: >- {%- else -%} python311 {%- endif -%} + +# The list of required Python Modules in packages +# This is required in order to avoid externally-managed-environment error. +__sap_software_download_python_module_packages: >- + {%- if ansible_distribution_major_version == '15' and ansible_distribution_version.split('.')[1] | int < 5 -%} + [ + "python3-wheel", + "python3-urllib3", + "python3-requests", + "python3-beautifulsoup4", + "python3-lxml" + ] + {%- else -%} + [ + "python311-wheel", + "python311-urllib3", + "python311-requests", + "python311-beautifulsoup4", + "python311-lxml" + ] + {%- endif -%} diff --git a/roles/sap_software_download/vars/main.yml b/roles/sap_software_download/vars/main.yml index 2897c1d..e40f28e 100644 --- a/roles/sap_software_download/vars/main.yml +++ b/roles/sap_software_download/vars/main.yml @@ -8,3 +8,13 @@ __sap_software_download_python_modules: - requests - beautifulsoup4 - lxml + +# The list of required Python Modules in packages +# This is required in order to avoid externally-managed-environment error. +# Specific packages are available in OS specific var files +__sap_software_download_python_module_packages: + - python3-wheel + - python3-urllib3 + - python3-requests + - python3-beautifulsoup4 + - python3-lxml From 8fde2eec212cca93a9f5665308de592f5806bb50 Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Wed, 2 Apr 2025 09:40:05 +0200 Subject: [PATCH 16/26] feat: readme rework and preparation for 1.2.0 --- CHANGELOG.rst | 61 +++++- README.md | 157 ++++++++------ docs/CONTRIBUTORS.md | 3 + docs/DEVELOPMENT.md | 78 ------- docs/EXEC_EXAMPLES.md | 134 ------------ docs/module_maintenance_planner_files.md | 138 ++++++++++++ ..._maintenance_planner_stack_xml_download.md | 135 ++++++++++++ docs/module_software_center_download.md | 202 ++++++++++++++++++ galaxy.yml | 16 +- 9 files changed, 626 insertions(+), 298 deletions(-) delete mode 100644 docs/DEVELOPMENT.md delete mode 100644 docs/EXEC_EXAMPLES.md diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ea6fdcd..77c45b4 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,36 +1,75 @@ =================================== -community.sap_launchpad Release Notes +community.sap\_launchpad Release Notes =================================== .. contents:: Topics +v1.2.0 +====== + +Release Summary +--------------- + +Enhancements to Modules and introduction of new Ansible Role. + +Minor Changes +------------- +- software_center_download: Add option to search for latest packages (https://github.com/sap-linuxlab/community.sap_launchpad/pull/28) +- maintenance_planner modules: Add option to use Display ID instead of name (https://github.com/sap-linuxlab/community.sap_launchpad/pull/31) +- sap_software_download: New role for downloading software (https://github.com/sap-linuxlab/community.sap_launchpad/pull/32) -v1.1.0 +Bugfixes +-------- + +- fix: cache gigya sdk build number (https://github.com/sap-linuxlab/community.sap_launchpad/pull/33) + + +v1.1.1 ====== Release Summary --------------- -| Release Date: 2023-11-28 -| Community contribution Ansible Module systems_info to query registered systems in me.sap.com -| Community contribution Ansible Module license_keys to create systems and license keys on me.sap.com/licensekey +Various bug fixes +Bugfixes +-------- +- Append logic for Account Temporarily Locked Out +- Fix errors in the example file -v1.0.1 + +v1.1.0 (2023-11-28) ====== Release Summary --------------- -| Release Date: 2023-09-14 -| Fix for handling old password prompt +Community contribution with new Ansible Modules `systems_info` and `license_keys`` + +Minor Changes +------------- + +- Create/update systems and license keys (https://github.com/sap-linuxlab/community.sap_launchpad/pull/16) + + +v1.0.1 (2023-09-14) +====== + +Release Summary +--------------- + +Various bug fixes + +Bugfixes +-------- + +- Fix for handling old password prompt -v1.0.0 +v1.0.0 (2023-08-22) ====== Release Summary --------------- -| Release Date: 2023-08-22 -| Initial Release on Galaxy +Initial Release on Galaxy diff --git a/README.md b/README.md index 3217a6a..03d48fa 100644 --- a/README.md +++ b/README.md @@ -1,108 +1,129 @@ -# community.sap_launchpad Ansible Collection ![Ansible Lint](https://github.com/sap-linuxlab/community.sap_launchpad/actions/workflows/ansible-lint.yml/badge.svg?branch=main) +# community.sap_launchpad Ansible Collection -This Ansible Collection executes basic SAP.com Support operations tasks. +![Ansible Lint](https://github.com/sap-linuxlab/community.sap_launchpad/actions/workflows/ansible-lint.yml/badge.svg?branch=main) -## Functionality +## Description -This Ansible Collection executes basic SAP.com Support operations tasks, including: +This Ansible Collection executes download of SAP Software. -- **Software Center Catalog** - - Search and Download of SAP software center catalog files - - Search and Extraction of SAP software center catalog information -- **Maintenance Planner** - - Lookup and download files from an existing 'New Implementation' MP Transaction and Stack, using SAP software center's download basket +Included role and modules cover range of options: +- Preparation of environment before download. +- Download of specific SAP Software files. +- Download of alternative SAP Software files if specific was not available. +- Download of SAP Software files from existing Maintenance Plan transaction. +- Download of Stack file from existing Maintenance Plan transaction. -## Contents -An Ansible Playbook can call either an Ansible Role, or the individual Ansible Modules for handling the API calls to various SAP Support Portal API capabilities: -- **Ansible Roles** (runs multiple Ansible Modules) -- **Ansible Modules** (and adjoining Python Functions) +## Requirements -For further information regarding the development, code structure and execution workflow please read the [Development documentation](./docs/DEVELOPMENT.md). +### Control Nodes +| Type | Version | +| :--- | :--- | +| Operating system | Any operating system with required Python and Ansible versions | +| Python | 3.11 or higher | +| Ansible | 9.9 or higher | +| Ansible-core | 2.16 or higher | -Within this Ansible Collection, there are various Ansible Modules. -#### Ansible Modules +### Managed Nodes +| Type | Version | +| :--- | :--- | +| Operating system | SUSE Linux Enterprise Server 15 SP5+, 16
Red Hat Enterprise Linux 8.x, 9.x, 10.x | +| Python | 3.11 or higher (SUSE)
3.9 or higher (Red Hat) | -| Name                    | Summary | -| :-- | :-- | -| [sap_launchpad.software_center_download](./docs/module_software_center_download.md) | search for files and download | -| [sap_launchpad.maintenance_planner_files](./docs/module_maintenance_planner_files.md) | maintenance planner files retrieval | -| [sap_launchpad.maintenance_planner_stack_xml_download](./docs/module_maintenance_planner_stack_xml_download.md) | maintenance planner stack xml download | +**NOTE: Operating system needs to have access to required package repositories either directly or via subscription registration.** -## Execution -### Credentials - SAP User ID +## Installation Instructions -SAP software installation media must be obtained from SAP directly, and requires valid license agreements with SAP in order to access these files. +### Installation +Install this collection with Ansible Galaxy command: +```console +ansible-galaxy collection install community.sap_launchpad +``` -An SAP Company Number (SCN) contains one or more Installation Number/s, providing licences for specified SAP Software. When an SAP User ID is created within the SAP Customer Number (SCN), the administrator must provide SAP Download authorizations for the SAP User ID. +Optionally you can include collection in requirements.yml file and include it together with other collections using: `ansible-galaxy collection install -r requirements.yml` +Requirements file need to be maintained in following format: +```yaml +collections: + - name: community.sap_launchpad +``` -When an SAP User ID (e.g. S-User) is enabled with and part of an SAP Universal ID, then the `sap_launchpad` Ansible Collection **must** use: -- the SAP User ID -- the password for login with the SAP Universal ID +### Upgrade +Installed Ansible Collection will not be upgraded automatically when Ansible package is upgraded. -In addition, if a SAP Universal ID is used then the recommendation is to check and reset the SAP User ID ‘Account Password’ in the [SAP Universal ID Account Manager](https://account.sap.com/manage/accounts), which will help to avoid any potential conflicts. +To upgrade the collection to the latest available version, run the following command: +```console +ansible-galaxy collection install community.sap_launchpad --upgrade +``` -For further information regarding connection errors, please see the FAQ section [Errors with prefix 'SAP SSO authentication failed - '](./docs/FAQ.md#errors-with-prefix-sap-sso-authentication-failed---). +You can also install a specific version of the collection, when you encounter issues with latest version. Please report these issues in affected Role repository if that happens. +Example of downgrading collection to version 1.0.0: +``` +ansible-galaxy collection install community.sap_launchpad:==1.0.0 +``` -### Execution examples +See [Installing collections](https://docs.ansible.com/ansible/latest/collections_guide/collections_installing.html) for more details on installation methods. -There are various methods to execute the Ansible Collection, dependant on the use case. For more information, see [Execution examples with code samples](./docs/EXEC_EXAMPLES.md) and the summary below: -| Execution Scenario | Use Case | Target | -| --- | --- | --- | -| Ansible Playbook
-> source Ansible Collection
-> execute Ansible Task
--> run Ansible Module
---> run Python/Bash Functions | Simple executions with a few activities | Localhost or Remote | -| Ansible Playbook
-> source Ansible Collection
-> execute Ansible Task
--> run Ansible Role
---> run Ansible Module
----> run Python/Bash Functions
--> run Ansible Role
---> ... | Complex executions with various interlinked activities;
run in parallel or sequentially | Localhost or Remote | -| Python/Bash Functions | Simple testing or non-Ansible use cases | Localhost | +## Contents ---- +### Ansible Modules +| Name | Summary | +| :-- | :-- | +| [sap_launchpad.software_center_download](./docs/module_software_center_download.md) | Search and download SAP Software file | +| [sap_launchpad.maintenance_planner_files](./docs/module_maintenance_planner_files.md) | Get list of files from Maintenance Planner | +| [sap_launchpad.maintenance_planner_stack_xml_download](./docs/module_maintenance_planner_stack_xml_download.md) | Get stack file from Maintenance Planner | -## Requirements, Dependencies and Testing +### Ansible Roles +| Name | Summary | +| :-- | :-- | +| [sap_software_download](./roles/sap_software_download/README.md) | Prepare environment and download SAP Software files or Maintenance Plan transaction files | -### Operating System requirements -Designed for Linux operating systems, e.g. RHEL. +## Testing +This Ansible Collection was tested across different Operating Systems and SAP products. -This role has not been tested and amended for SAP NetWeaver Application Server instantiations on IBM AIX or Windows Server. +| Type | Version | +| :--- | :--- | +| Operating system | SUSE Linux Enterprise Server 15 SP5+, 16
Red Hat Enterprise Linux 8.x, 9.x, 10.x | +| Python | 3.11, 3.12 | +| Ansible | 9, 10, 11 | +| Ansible-core | 2.16, 2.17, 2.18 | -Assumptions for executing this role include: -- Registered OS License and OS Package repositories are available (from the relevant content delivery network of the OS vendor) -- Simultaneous Ansible Playbook executions will require amendment of Ansible Variable name `softwarecenter_search_list` shown in execution samples (containing the list of installation media to download). This avoids accidental global variable clashes from occuring in Ansible Playbooks executed from the same controller host with an inline Ansible Inventory against 'all' target hosts. -### Python requirements +## Contributing +You can find more information about ways you can contribute at [sap-linuxlab website](https://sap-linuxlab.github.io/initiative_contributions/). -Execution/Controller/Management host: -- Python 3 -Target host: -- Python 3, with Python Modules `beautifulsoup4 lxml requests` (see [Execution examples with code samples](./docs/EXEC_EXAMPLES.md)) +## Support +You can report any issues using [Issues](https://github.com/sap-linuxlab/community.sap_launchpad/issues) section. -### Testing on execution/controller host -**Tests with Ansible Core release versions:** -- Ansible Core 2.11.5 community edition +## Release Notes and Roadmap +You can find the release notes of this collection in [Changelog file](./CHANGELOG.rst) -**Tests with Python release versions:** -- Python 3.9.7 (i.e. CPython distribution) -**Tests with Operating System release versions:** -- RHEL 8.4 -- macOS 11.6 (Big Sur), with Homebrew used for Python 3.x via PyEnv +## Further Information -### Testing on target/remote host +### Credentials - SAP S-User -**Tests with Operating System release versions:** -- RHEL 8.2 for SAP +SAP software files must be obtained from SAP directly, and requires valid license agreements with SAP in order to access these files. -**Tests with Python release versions:** -- Python 3.6.x (i.e. CPython distribution), default for RHEL 8.x and SLES 15.x -- Python 3.8.x (i.e. CPython distribution) +An SAP Company Number (SCN) contains one or more Installation Number/s, providing licenses for specified SAP Software. When an SAP User ID is created within the SAP Customer Number (SCN), the administrator must provide SAP Download authorizations for the SAP User ID. -## License +When an SAP User ID (e.g. S-User) is enabled with and part of an SAP Universal ID, then the `sap_launchpad` Ansible Collection **must** use: +- the SAP User ID +- the password for login with the SAP Universal ID -- [Apache 2.0](./LICENSE) +In addition, if a SAP Universal ID is used then the recommendation is to check and reset the SAP User ID ‘Account Password’ in the [SAP Universal ID Account Manager](https://account.sap.com/manage/accounts), which will help to avoid any potential conflicts. -## Contributors +For further information regarding connection errors, please see the FAQ section [Errors with prefix 'SAP SSO authentication failed - '](./docs/FAQ.md#errors-with-prefix-sap-sso-authentication-failed---). + +**Multi Factor Authentication is not supported.** -Contributors to the Ansible Roles within this Ansible Collection, are shown within [/docs/contributors](./docs/CONTRIBUTORS.md). +### Variable Precedence Rules +Please follow [Ansible Precedence guidelines](https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_variables.html#variable-precedence-where-should-i-put-a-variable) on how to pass variables when using this collection. + +## License +[Apache 2.0](./LICENSE) diff --git a/docs/CONTRIBUTORS.md b/docs/CONTRIBUTORS.md index 3905cb4..833ab8c 100644 --- a/docs/CONTRIBUTORS.md +++ b/docs/CONTRIBUTORS.md @@ -8,3 +8,6 @@ - **Sean Freeman** - Origin developer of Python API constructs, and project owner - **SVA GmbH - System Vertrieb Alexander GmbH** - **Rainer Leber** - User acceptance testing +- **SUSE** + - SUSE SAP Emerging Technology Solutions + - **Marcel Mamula** - Developer of Ansible Collection diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md deleted file mode 100644 index 20caca7..0000000 --- a/docs/DEVELOPMENT.md +++ /dev/null @@ -1,78 +0,0 @@ - -# Development of community.sap_launchpad Ansible Collection - -This Ansible Collection is developed with several design principles and code practices. - -## Code structure - -This Ansible Collection is heavily focused on Ansible Modules to perform required SAP Support API calls. The directory tree structure is shown below: -```code -collection/ -├── docs/ -├── meta/ -├── plugins/ -│ ├── modules/ -│ │ ├── users.py -│ │ ├── software_center_download.py -│ │ ├── software_center_catalog.py -│ │ ├── maintenance_planner_files.py -│ │ ├── maintenance_planner_stack_xml_download.py -│ │ ├── licenses.py -│ │ └── incidents.py -│ └── module_utils/ -│ ├── sap_id_sso.py -│ ├── sap_launchpad_software_center_download_runner.py -│ ├── sap_launchpad_software_center_catalog_runner.py -│ └── sap_launchpad_maintenance_planner_runner.py -├── roles/ -├── playbooks/ -│ ├── sample-download-install-media.yml -│ └── sample-maintenance-planner-download.yml -├── tests/ -├── galaxy.yml -└── README.md -``` - -## Execution logic - -This Ansible Collection is designed to be heavily re-usable for various SAP Support scenarios (both server-side and client-side), and avoid encapsulation of commands within Ansible's syntax; this ensures the scripts (and the sequence of commands) could be re-used manually or re-used by another automation framework. - -It is important to understand the execution flow by an Ansible Playbook to either an Ansible Role (with or without embedded Playbooks), an Ansible Task, or an Ansible Module (and contained Script files). Alternatively it is possible to call the script files manually. - - -See examples below: - -### Ansible Playbook to call many Ansible Roles (and the contained interlinked Ansible Tasks) -```code -# Produce outcome scenario, using many interlinked tasks -- Run: Ansible Playbook - - Run: Ansible Role - - Ansible Task - - Ansible Playbook 1..n - - Ansible Task - - execute custom Ansible Module - - execute specified Python Module Functions - - call APIs or CLIs/binaries - - Ansible Task - - Ansible Playbook 1..n - - Ansible Task - - subsequent OS commands using output from APIs or CLIs/binaries -``` - -### Ansible Playbook to call single set of Ansible Tasks -```code -# Produce outcome scenario, with single set of tasks -- Run: Ansible Playbook - - Ansible Task - - execute custom Ansible Module - - execute specified Python Module Functions - - call APIs or CLIs/binaries -``` - -### Python Shell to call single Python Function -```code -# Produce outcome scenario manually with singular code execution -- Run: Python Shell - - Import Python Module file for APIs or CLIs/binaries - - Execute specificed Python Functions -``` diff --git a/docs/EXEC_EXAMPLES.md b/docs/EXEC_EXAMPLES.md deleted file mode 100644 index 2288d4f..0000000 --- a/docs/EXEC_EXAMPLES.md +++ /dev/null @@ -1,134 +0,0 @@ -# Execution examples - -## Execution example with Ansible Playbook calling Ansible Module - -**Ansible Playbook YAML, execute Ansible Module** -```yaml ---- -- hosts: all - - collections: - - community.sap_launchpad - - pre_tasks: - - name: Install Python package manager pip3 to system Python - yum: - name: python3-pip - state: present - - name: Install Python dependencies for Ansible Modules to system Python - pip: - name: - - urllib3 - - requests - - beautifulsoup4 - - lxml - -# Prompt for Ansible Variables - vars_prompt: - - name: suser_id - prompt: Please enter S-User - private: no - - name: suser_password - prompt: Please enter Password - private: yes - -# Define Ansible Variables - vars: - ansible_python_interpreter: python3 - softwarecenter_search_list: - - 'SAPCAR_1324-80000936.EXE' - - 'HCMT_057_0-80003261.SAR' - -# Use task block to call Ansible Module - tasks: - - name: Execute Ansible Module to download SAP software - community.sap_launchpad.software_center_download: - suser_id: "{{ suser_id }}" - suser_password: "{{ suser_password }}" - softwarecenter_search_query: "{{ item }}" - dest: "/tmp/" - loop: "{{ softwarecenter_search_list }}" - loop_control: - label: "{{ item }} : {{ download_task.msg }}" - register: download_task - retries: 1 - until: download_task is not failed -``` - -**Execution of Ansible Playbook, with in-line Ansible Inventory set as localhost** - -```shell -# Install from local source directory for Ansible 2.11+ -ansible-galaxy collection install community.sap_launchpad - -# Workaround install from local source directory for Ansible 2.9.x -# mv ./community.sap_launchpad ~/.ansible/collections/ansible_collections/community - -# Run Ansible Collection on localhost -ansible-playbook --timeout 60 ./community.sap_launchpad/playbooks/sample-download-install-media.yml --inventory "localhost," --connection=local -``` - -**Execution of Ansible Playbook, with in-line Ansible Inventory of target/remote hosts** - -```shell -# Install from local source directory for Ansible 2.11+ -ansible-galaxy collection install ./community.sap_launchpad - -# Workaround install from local source directory for Ansible 2.9.x -# mv ./community.sap_launchpad ~/.ansible/collections/ansible_collections/community - -# SSH Connection details -bastion_private_key_file="$PWD/bastion_rsa" -bastion_host="169.0.40.4" -bastion_port="50222" -bastion_user="bastionuser" - -target_private_key_file="$PWD/vs_rsa" -target_host="10.0.50.5" -target_user="root" - -# Run Ansible Collection to target/remote hosts via Proxy/Bastion -ansible-playbook --timeout 60 ./sample-playbook.yml \ ---connection 'ssh' --user "$target_user" --inventory "$target_host," --private-key "$target_private_key_file" \ ---ssh-extra-args="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ProxyCommand='ssh -W %h:%p $bastion_user@$bastion_host -p $bastion_port -i $bastion_private_key_file -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null'" -``` - -## Execution example with Python environment - -**Setup local Python environment** -```shell -# Change directory to Python scripts source -cd ./plugins - -# Create isolated Python (protect system Python) -pyenv install 3.9.6 -pyenv virtualenv 3.9.6 sap_launchpad -pyenv activate sap_launchpad - -# Install Python Modules to current Python environment -pip3 install beautifulsoup4 lxml requests - -# Run Python, import Python Modules and run Python Functions -python3 -``` - -**Execute Python Functions** -```python ->>> from module_utils.sap_id_sso import sap_sso_login ->>> from module_utils.sap_launchpad_software_center_download_runner import * ->>> ->>> # Debug ->>> # from module_utils.sap_api_common import debug_https ->>> # debug_https() ->>> ->>> ## Perform API login requests to SAP Support ->>> username='S0000000' ->>> password='password' ->>> sap_sso_login(username, password) ->>> ## Perform API activity requests to SAP Support (e.g. software search without deduplication, and download software) ->>> query_result = search_software_filename("HCMT_057_0-80003261.SAR",'') ->>> download_software(*query_result, output_dir='/tmp') -... ->>> ## API responses from SAP Support ->>> exit() -``` diff --git a/docs/module_maintenance_planner_files.md b/docs/module_maintenance_planner_files.md index 285fdb5..080c612 100644 --- a/docs/module_maintenance_planner_files.md +++ b/docs/module_maintenance_planner_files.md @@ -1,2 +1,140 @@ # maintenance_planner_files Ansible Module +## Description +The Ansible Module `maintenance_planner_files` is used to obtain list of SAP Software files belonging to Maintenance Plan transaction. + +## Dependencies +This module requires the following Python modules to be installed on the target node (the machine where SAP software will be downloaded): + +- wheel +- urllib3 +- requests +- beautifulsoup4 +- lxml + +Installation instructions are available at [Installation of prerequisites](#installation-of-prerequisites) + +## Execution + +### Example +Obtain list of SAP Software files +```yaml +- name: Obtain list of SAP Software files + community.sap_launchpad.maintenance_planner_files: + suser_id: "Enter SAP S-User ID" + suser_password: "Enter SAP S-User Password" + transaction_name: "Transaction Name or Display ID from Maintenance Planner" + register: __module_results +``` + +Obtain list of SAP Software files using Python Virtual Environment `/tmp/python_venv` +```yaml +- name: Obtain list of SAP Software files using Python Virtual Environment + community.sap_launchpad.maintenance_planner_files: + suser_id: "Enter SAP S-User ID" + suser_password: "Enter SAP S-User Password" + transaction_name: "Transaction Name or Display ID from Maintenance Planner" + register: __module_results + environment: + PATH: "/tmp/python_venv:{{ ansible_env.PATH }}" + PYTHONPATH: "/tmp/python_venv/lib/python3.11/site-packages" + VIRTUAL_ENV: "/tmp/python_venv" + vars: + ansible_python_interpreter: "/tmp/python_venv/bin/python3.11 }}" +``` + +### Output format +#### msg +- _Type:_ `string`
+ +The status of execution. + +#### download_basket +- _Type:_ `list` with elements of type `dictionary`
+ +A Json list of software download links and filenames.
+```yml +- DirectLink: https://softwaredownloads.sap.com/file/0020000001739942021 + Filename: IMDB_SERVER20_060_0-80002031.SAR +- DirectLink: https://softwaredownloads.sap.com/file/0010000001440232021 + Filename: KD75379.SAR +``` + +## Further Information +### Installation of prerequisites +**All preparation steps are included in role `sap_launchpad.sap_software_download`.**
+ +Prerequisite preparation using Python 3.11 Virtual Environment `/tmp/python_venv` (Recommended) +```yaml +--- +- name: Example play to install prerequisites for sap_launchpad + hosts: all + pre_tasks: + - name: Install Python and Python package manager pip + ansible.builtin.package: + name: + - python311 + - python311-pip + state: present + + - name: Pre-Steps - Install Python modules to Python venv + ansible.builtin.pip: + name: + - wheel + - urllib3 + - requests + - beautifulsoup4 + - lxml + virtualenv: "/tmp/python_venv" + virtualenv_command: "python3.11 -m venv" +``` + +Prerequisite preparation using Python 3.11 system default
+```yaml +--- +- name: Example play to install prerequisites for sap_launchpad + hosts: all + pre_tasks: + - name: Install Python and Python package manager pip + ansible.builtin.package: + name: + - python311 + - python311-pip + state: present + + - name: Install Python module packages + ansible.builtin.package: + name: + - python311-wheel + - python311-urllib3 + - python311-requests + - python311-beautifulsoup4 + - python311-lxml + state: present +``` +**NOTE:** Python modules are installed as packages to avoid `externally-managed-environment` error. + +## License +Apache 2.0 + +## Maintainers +Maintainers are shown within [/docs/contributors](./CONTRIBUTORS.md). + +## Module Variables +### suser_id +- _Required:_ `true`
+- _Type:_ `string`
+ +The SAP S-User ID with download authorization for SAP software. + +### suser_password +- _Required:_ `true`
+- _Type:_ `string`
+ +The password for the SAP S-User specified in `suser_id`. + +### transaction_name +- _Required:_ `true`
+- _Type:_ `string`
+ +The name or display ID of a transaction from the SAP Maintenance Planner. diff --git a/docs/module_maintenance_planner_stack_xml_download.md b/docs/module_maintenance_planner_stack_xml_download.md index 066a120..2c161fe 100644 --- a/docs/module_maintenance_planner_stack_xml_download.md +++ b/docs/module_maintenance_planner_stack_xml_download.md @@ -1,2 +1,137 @@ # maintenance_planner_stack_xml_download Ansible Module +## Description +The Ansible Module `maintenance_planner_stack_xml_download` is used to obtain Stack file belonging to Maintenance Plan transaction. + +## Dependencies +This module requires the following Python modules to be installed on the target node (the machine where SAP software will be downloaded): + +- wheel +- urllib3 +- requests +- beautifulsoup4 +- lxml + +Installation instructions are available at [Installation of prerequisites](#installation-of-prerequisites) + +## Execution + +### Example +Obtain Stack file +```yaml +- name: Obtain Stack file + community.sap_launchpad.maintenance_planner_stack_xml_download: + suser_id: "Enter SAP S-User ID" + suser_password: "Enter SAP S-User Password" + transaction_name: "Transaction Name or Display ID from Maintenance Planner" + download_path: "/software" + register: __module_results +``` + +Obtain Stack file using Python Virtual Environment `/tmp/venv` +```yaml +- name: Obtain Stack file using Python Virtual Environment + community.sap_launchpad.maintenance_planner_stack_xml_download: + suser_id: "Enter SAP S-User ID" + suser_password: "Enter SAP S-User Password" + transaction_name: "Transaction Name or Display ID from Maintenance Planner" + download_path: "/software" + register: __module_results + environment: + PATH: "/tmp/venv:{{ ansible_env.PATH }}" + PYTHONPATH: "/tmp/venv/lib/python3.11/site-packages" + VIRTUAL_ENV: "/tmp/venv" + vars: + ansible_python_interpreter: "/tmp/venv/bin/python3.11 }}" +``` + +### Output format +#### msg +- _Type:_ `string`
+ +The status of execution. + +## Further Information +### Installation of prerequisites +**All preparation steps are included in role `sap_launchpad.sap_software_download`.**
+ +Prerequisite preparation using Python 3.11 Virtual Environment `/tmp/python_venv` (Recommended) +```yaml +--- +- name: Example play to install prerequisites for sap_launchpad + hosts: all + pre_tasks: + - name: Install Python and Python package manager pip + ansible.builtin.package: + name: + - python311 + - python311-pip + state: present + + - name: Pre-Steps - Install Python modules to Python venv + ansible.builtin.pip: + name: + - wheel + - urllib3 + - requests + - beautifulsoup4 + - lxml + virtualenv: "/tmp/python_venv" + virtualenv_command: "python3.11 -m venv" +``` + +Prerequisite preparation using Python 3.11 system default
+```yaml +--- +- name: Example play to install prerequisites for sap_launchpad + hosts: all + pre_tasks: + - name: Install Python and Python package manager pip + ansible.builtin.package: + name: + - python311 + - python311-pip + state: present + + - name: Install Python module packages + ansible.builtin.package: + name: + - python311-wheel + - python311-urllib3 + - python311-requests + - python311-beautifulsoup4 + - python311-lxml + state: present +``` +**NOTE:** Python modules are installed as packages to avoid `externally-managed-environment` error. + +## License +Apache 2.0 + +## Maintainers +Maintainers are shown within [/docs/contributors](./CONTRIBUTORS.md). + +## Module Variables +### suser_id +- _Required:_ `true`
+- _Type:_ `string`
+ +The SAP S-User ID with download authorization for SAP software. + +### suser_password +- _Required:_ `true`
+- _Type:_ `string`
+ +The password for the SAP S-User specified in `suser_id`. + +### transaction_name +- _Required:_ `true`
+- _Type:_ `string`
+ +The name or display ID of a transaction from the SAP Maintenance Planner. + +### download_path +- _Required:_ `true`
+- _Type:_ `string`
+ +The directory where downloaded SAP software files will be stored. diff --git a/docs/module_software_center_download.md b/docs/module_software_center_download.md index 8b7c939..106a959 100644 --- a/docs/module_software_center_download.md +++ b/docs/module_software_center_download.md @@ -1,2 +1,204 @@ # software_center_download Ansible Module +## Description +The Ansible Module `software_center_download` is used to download SAP Software file from SAP. + +## Dependencies +This module requires the following Python modules to be installed on the target node (the machine where SAP software will be downloaded): + +- wheel +- urllib3 +- requests +- beautifulsoup4 +- lxml + +Installation instructions are available at [Installation of prerequisites](#installation-of-prerequisites) + +## Execution + +### Example +Download SAP Software file +```yaml +- name: Download SAP Software file + community.sap_launchpad.software_center_download: + suser_id: "Enter SAP S-User ID" + suser_password: "Enter SAP S-User Password" + search_query: "Enter SAP Software file name" + download_path: "Enter download path (e.g. /software)" +``` + +Download SAP Software file, but search for alternatives if not found +```yaml +- name: Download SAP Software file with alternative + community.sap_launchpad.software_center_download: + suser_id: "Enter SAP S-User ID" + suser_password: "Enter SAP S-User Password" + search_query: "Enter SAP Software file name" + download_path: "Enter download path (e.g. /software)" + search_alternatives: true + deduplicate: "last" +``` + +Download list of SAP Software files, but search for alternatives if not found +```yaml +- name: Download list of SAP Software files + community.sap_launchpad.software_center_download: + suser_id: "Enter SAP S-User ID" + suser_password: "Enter SAP S-User Password" + search_query: "{{ item }}" + download_path: "Enter download path (e.g. /software)" + search_alternatives: true + deduplicate: "last" + loop: + - "Enter SAP Software file name 1" + - "Enter SAP Software file name 2" + loop_control: + label: "{{ item }} : {{ __module_results.msg | d('') }}" + register: __module_results + retries: 1 + until: __module_results is not failed +``` + +Download SAP Software file using Python Virtual Environment `/tmp/venv` +```yaml +- name: Download list of SAP Software files + community.sap_launchpad.software_center_download: + suser_id: "Enter SAP S-User ID" + suser_password: "Enter SAP S-User Password" + search_query: "{{ item }}" + download_path: "Enter download path (e.g. /software)" + loop: + - "Enter SAP Software file name 1" + - "Enter SAP Software file name 2" + loop_control: + label: "{{ item }} : {{ __module_results.msg | d('') }}" + register: __module_results + retries: 1 + until: __module_results is not failed + environment: + PATH: "/tmp/venv:{{ ansible_env.PATH }}" + PYTHONPATH: "/tmp/venv/lib/python3.11/site-packages" + VIRTUAL_ENV: "/tmp/venv" + vars: + ansible_python_interpreter: "/tmp/venv/bin/python3.11 }}" +``` + +### Output format +#### msg +- _Type:_ `string`
+ +The status of execution. + +## Further Information +### Installation of prerequisites +**All preparation steps are included in role `sap_launchpad.sap_software_download`.**
+ +Prerequisite preparation using Python 3.11 Virtual Environment `/tmp/python_venv` (Recommended) +```yaml +--- +- name: Example play to install prerequisites for sap_launchpad + hosts: all + pre_tasks: + - name: Install Python and Python package manager pip + ansible.builtin.package: + name: + - python311 + - python311-pip + state: present + + - name: Pre-Steps - Install Python modules to Python venv + ansible.builtin.pip: + name: + - wheel + - urllib3 + - requests + - beautifulsoup4 + - lxml + virtualenv: "/tmp/python_venv" + virtualenv_command: "python3.11 -m venv" +``` + +Prerequisite preparation using Python 3.11 system default
+```yaml +--- +- name: Example play to install prerequisites for sap_launchpad + hosts: all + pre_tasks: + - name: Install Python and Python package manager pip + ansible.builtin.package: + name: + - python311 + - python311-pip + state: present + + - name: Install Python module packages + ansible.builtin.package: + name: + - python311-wheel + - python311-urllib3 + - python311-requests + - python311-beautifulsoup4 + - python311-lxml + state: present +``` +**NOTE:** Python modules are installed as packages to avoid `externally-managed-environment` error. + +## License +Apache 2.0 + +## Maintainers +Maintainers are shown within [/docs/contributors](./CONTRIBUTORS.md). + +## Module Variables +### suser_id +- _Required:_ `true`
+- _Type:_ `string`
+ +The SAP S-User ID with download authorization for SAP software. + +### suser_password +- _Required:_ `true`
+- _Type:_ `string`
+ +The password for the SAP S-User specified in `suser_id`. + +### search_query +- _Type:_ `string`
+ +The SAP software file name to download. + +### download_link +- _Type:_ `string`
+ +Direct download link to the SAP software.
+Download links can be obtained from SAP Software Center or using module `module_maintenance_planner_files`. + +### download_filename +- _Type:_ `string`
+ +Download filename of the SAP software.
+Download names can be obtained from SAP Software Center or using module `module_maintenance_planner_files`. + +### download_path +- _Required:_ `true`
+- _Type:_ `string`
+ +The directory where downloaded SAP software files will be stored. + +### deduplicate +- _Type:_ `string`
+ +Specifies how to handle duplicate file results.
+If multiple files with the same name are found, this setting determines which one to download.
+- `first`: Download the first file found
+- `last`: Download the last file found.
+ +### search_alternatives +- _Type:_ `boolean`
+ +Enables searching for alternative files if the requested file is not found.
+ +### dry_run +- _Type:_ `boolean + +Check availability of SAP Software without downloading.
diff --git a/galaxy.yml b/galaxy.yml index 90db301..d54a14c 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -8,21 +8,21 @@ namespace: community name: sap_launchpad # The version of the collection. Must be compatible with semantic versioning -version: 1.1.0 +version: 1.2.0 # The path to the Markdown (.md) readme file. This path is relative to the root of the collection readme: README.md # A list of the collection's content authors. Can be just the name or in the format 'Full Name (url) authors: - - IBM Lab for SAP Solutions - - IBM Cloud for SAP - - IBM Consulting for SAP - - MatthiasWinzeler + - Sean Freeman + - Sheng Li Zhu + - Matthias Winzeler + - Marcel Mamula ### OPTIONAL but strongly recommended # A short summary description of the collection -description: Collection of Ansible Modules for SAP Launchpad APIs +description: Collection of Ansible Modules and Roles for downloading SAP Software # Either a single license or a list of licenses for content inside of a collection. Ansible Galaxy currently only # accepts L(SPDX,https://spdx.org/licenses/) licenses. This key is mutually exclusive with 'license_file' @@ -35,7 +35,9 @@ license_file: '' # A list of tags you want to associate with the collection for indexing/searching. A tag name has the same character # requirements as 'namespace' and 'name' -tags: [] +tags: + - sap + - download # Collections that this collection requires to be installed for it to be usable. The key of the dict is the # collection label 'namespace.name'. The value is a version range From a69f5fcfdf4adab3caeb19388be7dc01c2b0c93b Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Wed, 2 Apr 2025 09:47:01 +0200 Subject: [PATCH 17/26] update changelog with release PR --- CHANGELOG.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 77c45b4..e203b4b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -17,6 +17,7 @@ Minor Changes - software_center_download: Add option to search for latest packages (https://github.com/sap-linuxlab/community.sap_launchpad/pull/28) - maintenance_planner modules: Add option to use Display ID instead of name (https://github.com/sap-linuxlab/community.sap_launchpad/pull/31) - sap_software_download: New role for downloading software (https://github.com/sap-linuxlab/community.sap_launchpad/pull/32) +- Collection Readme update and preparation for 1.2.0 release (https://github.com/sap-linuxlab/community.sap_launchpad/pull/34) Bugfixes -------- From a9868329d09868066e0df1ba5353b0349a142c0a Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Wed, 2 Apr 2025 15:44:37 +0200 Subject: [PATCH 18/26] fix: ansible-lint corrections --- .ansible-lint | 2 +- .gitignore | 1 + roles/sap_software_download/defaults/main.yml | 16 ++++++++-------- .../sap_software_download/meta/argument_spec.yml | 4 ++-- .../tasks/download_files.yml | 6 +++--- .../tasks/download_plan.yml | 6 +++--- .../tasks/pre_steps/01_include_variables.yml | 6 +++--- .../tasks/pre_steps/03_validate_credentials.yml | 6 +++--- .../tasks/pre_steps/04_get_plan_files.yml | 6 +++--- .../tasks/pre_steps/05_validate_relations.yml | 6 +++--- .../tasks/pre_steps/06_validate_sap_hana_1.yml | 2 +- 11 files changed, 31 insertions(+), 30 deletions(-) diff --git a/.ansible-lint b/.ansible-lint index 829e107..ff93a8f 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -5,7 +5,7 @@ exclude_paths: - .ansible/ - .cache/ - .github/ - #- docs/ + # - docs/ - changelogs/ - playbooks/ - tests/ diff --git a/.gitignore b/.gitignore index 4938417..9ff6291 100644 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,4 @@ __pycache__/ # VSCode .vscode +.ansible diff --git a/roles/sap_software_download/defaults/main.yml b/roles/sap_software_download/defaults/main.yml index cc2fcab..50ac01f 100644 --- a/roles/sap_software_download/defaults/main.yml +++ b/roles/sap_software_download/defaults/main.yml @@ -33,7 +33,7 @@ sap_software_download_use_venv: true # The SAP S-User ID with download authorization for SAP software. sap_software_download_suser_id: '' -# The password for the SAP S-User specified in `sap_software_download_suser_id`. +# The password for the SAP S-User specified in `sap_software_download_suser_id`. sap_software_download_suser_password: '' # A list of SAP software file names to download. @@ -44,19 +44,13 @@ sap_software_download_files: [] sap_software_download_mp_transaction: '' # Enables searching for alternative files if the requested file is not found. -# Only applies to files specified in `sap_software_download_files`. +# Only applies to files specified in `sap_software_download_files`. # If set to `false`, the role will not search for alternatives. sap_software_download_find_alternatives: true # The directory where downloaded SAP software files will be stored. sap_software_download_directory: '/software' -# Enables validation of relationships between SAP software files. -# Only applies to files specified in `sap_software_download_files`. -# If set to `false`, no relationship validation will be performed. -# Example: Verify version of IMDB_LCAPPS against IMDB_SERVER if IMDB_SERVER was found. -sap_software_download_validate_relationships: true - # Determines whether to ignore errors when a requested file is not found. # If set to `true`, the role will continue execution and download other files, even if some files are not found. # If set to `false`, the role will fail if any requested file is not found. @@ -68,6 +62,12 @@ sap_software_download_ignore_file_not_found: false # If set to `false`, the role will fail if the specified Maintenance Plan is not found. sap_software_download_ignore_plan_not_found: false +# Enables validation of relationships between SAP software files. +# Only applies to files specified in `sap_software_download_files`. +# If set to `false`, no relationship validation will be performed. +# Example: Verify version of IMDB_LCAPPS against IMDB_SERVER if IMDB_SERVER was found. +sap_software_download_validate_relationships: true + # Determines whether to ignore warnings during file relationship validation. # If set to `true`, the role will continue execution even if there are warnings during the validation of file relationships. # If set to `false`, the role will fail if any warnings are encountered during file relationship validation. diff --git a/roles/sap_software_download/meta/argument_spec.yml b/roles/sap_software_download/meta/argument_spec.yml index adfb6c9..6a64e3e 100644 --- a/roles/sap_software_download/meta/argument_spec.yml +++ b/roles/sap_software_download/meta/argument_spec.yml @@ -64,7 +64,7 @@ argument_specs: required: true no_log: true description: - - The password for the SAP S-User specified in `sap_software_download_suser_id`. + - The password for the SAP S-User specified in `sap_software_download_suser_id`. sap_software_download_files: type: list @@ -88,7 +88,7 @@ argument_specs: default: true description: - Enables searching for alternative files if the requested file is not found. - - Only applies to files specified in `sap_software_download_files`. + - Only applies to files specified in `sap_software_download_files`. - If set to `false`, the role will not search for alternatives. sap_software_download_directory: diff --git a/roles/sap_software_download/tasks/download_files.yml b/roles/sap_software_download/tasks/download_files.yml index 6afcc76..f209caf 100644 --- a/roles/sap_software_download/tasks/download_files.yml +++ b/roles/sap_software_download/tasks/download_files.yml @@ -17,9 +17,9 @@ retries: 1 until: __sap_software_download_files_results_venv is not failed environment: - PATH: "{{ __sap_software_download_venv.path }}/bin:{{ ansible_env.PATH }}" - PYTHONPATH: "{{ __sap_software_download_venv.path }}/lib/{{ sap_software_download_python_interpreter }}/site-packages" - VIRTUAL_ENV: "{{ __sap_software_download_venv.path }}" + PATH: "{{ __sap_software_download_venv.path }}/bin:{{ ansible_env.PATH }}" + PYTHONPATH: "{{ __sap_software_download_venv.path }}/lib/{{ sap_software_download_python_interpreter }}/site-packages" + VIRTUAL_ENV: "{{ __sap_software_download_venv.path }}" vars: ansible_python_interpreter: "{{ __sap_software_download_venv.path ~ '/bin/' ~ sap_software_download_python_interpreter }}" ignore_errors: true # Errors are ignored and validated afterwards diff --git a/roles/sap_software_download/tasks/download_plan.yml b/roles/sap_software_download/tasks/download_plan.yml index ecd8565..a301294 100644 --- a/roles/sap_software_download/tasks/download_plan.yml +++ b/roles/sap_software_download/tasks/download_plan.yml @@ -16,9 +16,9 @@ retries: 1 until: __sap_software_download_files_plan_results_venv is not failed environment: - PATH: "{{ __sap_software_download_venv.path }}/bin:{{ ansible_env.PATH }}" - PYTHONPATH: "{{ __sap_software_download_venv.path }}/lib/{{ sap_software_download_python_interpreter }}/site-packages" - VIRTUAL_ENV: "{{ __sap_software_download_venv.path }}" + PATH: "{{ __sap_software_download_venv.path }}/bin:{{ ansible_env.PATH }}" + PYTHONPATH: "{{ __sap_software_download_venv.path }}/lib/{{ sap_software_download_python_interpreter }}/site-packages" + VIRTUAL_ENV: "{{ __sap_software_download_venv.path }}" vars: ansible_python_interpreter: "{{ __sap_software_download_venv.path ~ '/bin/' ~ sap_software_download_python_interpreter }}" ignore_errors: true # Errors are ignored and validated afterwards diff --git a/roles/sap_software_download/tasks/pre_steps/01_include_variables.yml b/roles/sap_software_download/tasks/pre_steps/01_include_variables.yml index 2eeac53..edfec13 100644 --- a/roles/sap_software_download/tasks/pre_steps/01_include_variables.yml +++ b/roles/sap_software_download/tasks/pre_steps/01_include_variables.yml @@ -74,7 +74,7 @@ - sap_software_download_python_package | length > 0 fail_msg: | Empty variable `sap_software_download_python_package`. - Ensure that it contains correct Python package name or revert back to defaults. + Ensure that it contains correct Python package name or revert back to defaults. - name: "Pre-Steps - Verify variable: sap_software_download_python_module_packages" ansible.builtin.assert: @@ -84,7 +84,7 @@ - sap_software_download_python_module_packages | length > 0 fail_msg: | Empty variable `sap_software_download_python_module_packages`. - Ensure that it contains correct list of Python module package names or revert back to defaults. + Ensure that it contains correct list of Python module package names or revert back to defaults. - name: "Pre-Steps - Verify variable: sap_software_download_suser_id" ansible.builtin.assert: @@ -93,7 +93,7 @@ - sap_software_download_suser_id | length > 0 fail_msg: | Empty variable `sap_software_download_suser_id`. - Enter valid S-User ID with download authorizations. + Enter valid S-User ID with download authorizations. - name: "Pre-Steps - Verify variable: sap_software_download_suser_password" ansible.builtin.assert: diff --git a/roles/sap_software_download/tasks/pre_steps/03_validate_credentials.yml b/roles/sap_software_download/tasks/pre_steps/03_validate_credentials.yml index 51a9801..0769386 100644 --- a/roles/sap_software_download/tasks/pre_steps/03_validate_credentials.yml +++ b/roles/sap_software_download/tasks/pre_steps/03_validate_credentials.yml @@ -16,9 +16,9 @@ retries: 1 delay: 5 environment: - PATH: "{{ __sap_software_download_venv.path }}/bin:{{ ansible_env.PATH }}" - PYTHONPATH: "{{ __sap_software_download_venv.path }}/lib/{{ sap_software_download_python_interpreter }}/site-packages" - VIRTUAL_ENV: "{{ __sap_software_download_venv.path }}" + PATH: "{{ __sap_software_download_venv.path }}/bin:{{ ansible_env.PATH }}" + PYTHONPATH: "{{ __sap_software_download_venv.path }}/lib/{{ sap_software_download_python_interpreter }}/site-packages" + VIRTUAL_ENV: "{{ __sap_software_download_venv.path }}" vars: ansible_python_interpreter: "{{ __sap_software_download_venv.path ~ '/bin/' ~ sap_software_download_python_interpreter }}" ignore_errors: true # Errors are ignored and validated afterwards diff --git a/roles/sap_software_download/tasks/pre_steps/04_get_plan_files.yml b/roles/sap_software_download/tasks/pre_steps/04_get_plan_files.yml index 11e8c4c..a4baef1 100644 --- a/roles/sap_software_download/tasks/pre_steps/04_get_plan_files.yml +++ b/roles/sap_software_download/tasks/pre_steps/04_get_plan_files.yml @@ -10,9 +10,9 @@ register: __sap_software_download_mp_transaction_results_venv retries: 1 environment: - PATH: "{{ __sap_software_download_venv.path }}/bin:{{ ansible_env.PATH }}" - PYTHONPATH: "{{ __sap_software_download_venv.path }}/lib/{{ sap_software_download_python_interpreter }}/site-packages" - VIRTUAL_ENV: "{{ __sap_software_download_venv.path }}" + PATH: "{{ __sap_software_download_venv.path }}/bin:{{ ansible_env.PATH }}" + PYTHONPATH: "{{ __sap_software_download_venv.path }}/lib/{{ sap_software_download_python_interpreter }}/site-packages" + VIRTUAL_ENV: "{{ __sap_software_download_venv.path }}" vars: ansible_python_interpreter: "{{ __sap_software_download_venv.path ~ '/bin/' ~ sap_software_download_python_interpreter }}" changed_when: false # Getting list of files does not change anything diff --git a/roles/sap_software_download/tasks/pre_steps/05_validate_relations.yml b/roles/sap_software_download/tasks/pre_steps/05_validate_relations.yml index b0b0915..5dc195e 100644 --- a/roles/sap_software_download/tasks/pre_steps/05_validate_relations.yml +++ b/roles/sap_software_download/tasks/pre_steps/05_validate_relations.yml @@ -22,9 +22,9 @@ retries: 1 until: __sap_software_download_files_results_dryrun_venv is not failed environment: - PATH: "{{ __sap_software_download_venv.path }}/bin:{{ ansible_env.PATH }}" - PYTHONPATH: "{{ __sap_software_download_venv.path }}/lib/{{ sap_software_download_python_interpreter }}/site-packages" - VIRTUAL_ENV: "{{ __sap_software_download_venv.path }}" + PATH: "{{ __sap_software_download_venv.path }}/bin:{{ ansible_env.PATH }}" + PYTHONPATH: "{{ __sap_software_download_venv.path }}/lib/{{ sap_software_download_python_interpreter }}/site-packages" + VIRTUAL_ENV: "{{ __sap_software_download_venv.path }}" vars: ansible_python_interpreter: "{{ __sap_software_download_venv.path ~ '/bin/' ~ sap_software_download_python_interpreter }}" ignore_errors: true # Errors are ignored and validated afterwards diff --git a/roles/sap_software_download/tasks/pre_steps/06_validate_sap_hana_1.yml b/roles/sap_software_download/tasks/pre_steps/06_validate_sap_hana_1.yml index 9a72e54..93ac892 100644 --- a/roles/sap_software_download/tasks/pre_steps/06_validate_sap_hana_1.yml +++ b/roles/sap_software_download/tasks/pre_steps/06_validate_sap_hana_1.yml @@ -24,7 +24,7 @@ Warning: Incompatible SAP HANA component LCAPPS files were found for detected SAP HANA DATABASE 1.0 SPS {{ __sap_software_download_sap_hana_1_version[:2] }} Revision {{ __sap_software_download_sap_hana_1_version }}.{{ __sap_software_download_sap_hana_1_revision }}. - + Expected file pattern: IMDB_LCAPPS_{{ __sap_software_download_sap_hana_1_version }}*_{{__sap_software_download_sap_hana_1_revision }}* Actual files detected: {{ __sap_software_download_sap_hana_1_lcapps_list | unique | join(', ') }} From bd62af78551b42f0e7ed44a594971f8c783c7bab Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Wed, 2 Apr 2025 15:46:01 +0200 Subject: [PATCH 19/26] fix: ansible-lint corrections --- galaxy.yml | 17 +++++++++++++++-- requirements.yml | 3 +++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/galaxy.yml b/galaxy.yml index d54a14c..a426fc3 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -27,7 +27,7 @@ description: Collection of Ansible Modules and Roles for downloading SAP Softwar # Either a single license or a list of licenses for content inside of a collection. Ansible Galaxy currently only # accepts L(SPDX,https://spdx.org/licenses/) licenses. This key is mutually exclusive with 'license_file' license: -- Apache-2.0 + - Apache-2.0 # The path to the license file for the collection. This path is relative to the root of the collection. This key is # mutually exclusive with 'license' @@ -38,6 +38,8 @@ license_file: '' tags: - sap - download + - application + - tools # Collections that this collection requires to be installed for it to be usable. The key of the dict is the # collection label 'namespace.name'. The value is a version range @@ -61,4 +63,15 @@ issues: https://github.com/sap-linuxlab/community.sap_launchpad/issues # artifact. A pattern is matched from the relative path of the file or directory of the collection directory. This # uses 'fnmatch' to match the files or directories. Some directories and files like 'galaxy.yml', '*.pyc', '*.retry', # and '.git' are always filtered -build_ignore: ['tests', 'internal-*'] +build_ignore: + - 'tests' + - 'roles/*/tests' + - '.git*' + - 'roles/.git*' + - 'roles/*/.git*' + - '.ansible-lint' + - 'roles/*/.ansible-lint' + - '.yamllint*' + - 'roles/*/.yamllint*' + - '.pylintrc*' + - 'bindep*' diff --git a/requirements.yml b/requirements.yml index ed97d53..1d00cb5 100644 --- a/requirements.yml +++ b/requirements.yml @@ -1 +1,4 @@ --- +# There are no requirements defined for collection. +collections: [] +roles: [] From cc9e7cec8c68fd454ade507c74079df1571de57d Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Thu, 3 Apr 2025 11:47:08 +0200 Subject: [PATCH 20/26] revert back download_path to dest --- docs/module_maintenance_planner_stack_xml_download.md | 6 +++--- docs/module_software_center_download.md | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/module_maintenance_planner_stack_xml_download.md b/docs/module_maintenance_planner_stack_xml_download.md index 2c161fe..01fd249 100644 --- a/docs/module_maintenance_planner_stack_xml_download.md +++ b/docs/module_maintenance_planner_stack_xml_download.md @@ -24,7 +24,7 @@ Obtain Stack file suser_id: "Enter SAP S-User ID" suser_password: "Enter SAP S-User Password" transaction_name: "Transaction Name or Display ID from Maintenance Planner" - download_path: "/software" + dest: "/software" register: __module_results ``` @@ -35,7 +35,7 @@ Obtain Stack file using Python Virtual Environment `/tmp/venv` suser_id: "Enter SAP S-User ID" suser_password: "Enter SAP S-User Password" transaction_name: "Transaction Name or Display ID from Maintenance Planner" - download_path: "/software" + dest: "/software" register: __module_results environment: PATH: "/tmp/venv:{{ ansible_env.PATH }}" @@ -130,7 +130,7 @@ The password for the SAP S-User specified in `suser_id`. The name or display ID of a transaction from the SAP Maintenance Planner. -### download_path +### dest - _Required:_ `true`
- _Type:_ `string`
diff --git a/docs/module_software_center_download.md b/docs/module_software_center_download.md index 106a959..e4a8efa 100644 --- a/docs/module_software_center_download.md +++ b/docs/module_software_center_download.md @@ -24,7 +24,7 @@ Download SAP Software file suser_id: "Enter SAP S-User ID" suser_password: "Enter SAP S-User Password" search_query: "Enter SAP Software file name" - download_path: "Enter download path (e.g. /software)" + dest: "Enter download path (e.g. /software)" ``` Download SAP Software file, but search for alternatives if not found @@ -34,7 +34,7 @@ Download SAP Software file, but search for alternatives if not found suser_id: "Enter SAP S-User ID" suser_password: "Enter SAP S-User Password" search_query: "Enter SAP Software file name" - download_path: "Enter download path (e.g. /software)" + dest: "Enter download path (e.g. /software)" search_alternatives: true deduplicate: "last" ``` @@ -46,7 +46,7 @@ Download list of SAP Software files, but search for alternatives if not found suser_id: "Enter SAP S-User ID" suser_password: "Enter SAP S-User Password" search_query: "{{ item }}" - download_path: "Enter download path (e.g. /software)" + dest: "Enter download path (e.g. /software)" search_alternatives: true deduplicate: "last" loop: @@ -66,7 +66,7 @@ Download SAP Software file using Python Virtual Environment `/tmp/venv` suser_id: "Enter SAP S-User ID" suser_password: "Enter SAP S-User Password" search_query: "{{ item }}" - download_path: "Enter download path (e.g. /software)" + dest: "Enter download path (e.g. /software)" loop: - "Enter SAP Software file name 1" - "Enter SAP Software file name 2" @@ -179,7 +179,7 @@ Download links can be obtained from SAP Software Center or using module `module_ Download filename of the SAP software.
Download names can be obtained from SAP Software Center or using module `module_maintenance_planner_files`. -### download_path +### dest - _Required:_ `true`
- _Type:_ `string`
From 45adb7621cb2bc6b7fed70587f907e9c83b75e8a Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Thu, 3 Apr 2025 11:49:03 +0200 Subject: [PATCH 21/26] revert download_path to dest --- .../maintenance_planner_stack_xml_download.py | 10 +++--- plugins/modules/software_center_download.py | 32 +++++++++---------- roles/.gitkeep | 0 .../tasks/download_files.yml | 4 +-- .../tasks/download_plan.yml | 4 +-- .../pre_steps/03_validate_credentials.yml | 4 +-- .../tasks/pre_steps/05_validate_relations.yml | 4 +-- 7 files changed, 29 insertions(+), 29 deletions(-) delete mode 100644 roles/.gitkeep diff --git a/plugins/modules/maintenance_planner_stack_xml_download.py b/plugins/modules/maintenance_planner_stack_xml_download.py index 3f444db..6e958eb 100644 --- a/plugins/modules/maintenance_planner_stack_xml_download.py +++ b/plugins/modules/maintenance_planner_stack_xml_download.py @@ -30,7 +30,7 @@ - Transaction Name or Transaction Display ID from Maintenance Planner. required: true type: str - download_path: + dest: description: - Destination folder path. required: true @@ -46,7 +46,7 @@ suser_id: 'SXXXXXXXX' suser_password: 'password' transaction_name: 'MP_NEW_INST_20211015_044854' - download_path: "/tmp/" + dest: "/tmp/" register: sap_mp_register - name: Display the list of download links and filenames debug: @@ -79,7 +79,7 @@ def run_module(): suser_id=dict(type='str', required=True), suser_password=dict(type='str', required=True, no_log=True), transaction_name=dict(type='str', required=True), - download_path=dict(type='str', required=True) + dest=dict(type='str', required=True) ) # Define result dictionary objects to be passed back to Ansible @@ -102,7 +102,7 @@ def run_module(): username = module.params.get('suser_id') password = module.params.get('suser_password') transaction_name = module.params.get('transaction_name') - download_path = module.params.get('download_path') + dest = module.params.get('dest') # Main run @@ -118,7 +118,7 @@ def run_module(): transaction_id = get_transaction_id(transaction_name) # EXEC: Download the MP Stack XML file - get_transaction_stack_xml(transaction_id, download_path) + get_transaction_stack_xml(transaction_id, dest) # Process return dictionary for Ansible result['changed'] = True diff --git a/plugins/modules/software_center_download.py b/plugins/modules/software_center_download.py index 4e1fc79..5b909e8 100644 --- a/plugins/modules/software_center_download.py +++ b/plugins/modules/software_center_download.py @@ -48,7 +48,7 @@ - Download filename of the SAP software. required: false type: str - download_path: + dest: description: - Destination folder path. required: true @@ -80,14 +80,14 @@ suser_password: 'password' search_query: - 'SAPCAR_1324-80000936.EXE' - download_path: "/tmp/" + dest: "/tmp/" - name: Download using direct link and filename community.sap_launchpad.software_center_download: suser_id: 'SXXXXXXXX' suser_password: 'password' download_link: 'https://softwaredownloads.sap.com/file/0010000000048502015' download_filename: 'IW_FNDGC100.SAR' - download_path: "/tmp/" + dest: "/tmp/" ''' RETURN = r''' @@ -116,21 +116,21 @@ from ..module_utils.sap_launchpad_software_center_download_runner import * from ..module_utils.sap_id_sso import sap_sso_login -def _check_similar_files(download_path, filename): +def _check_similar_files(dest, filename): """ Checks for similar files in the download path based on the given filename. Args: - download_path (str): The path where files are downloaded. + dest (str): The path where files are downloaded. filename (str): The filename to check for similar files. Returns: bool: True if similar files exist, False otherwise. filename_similar_names: A list of similar filenames if they exist, empty list otherwise. """ - # pattern = download_path + '/**/' + os.path.splitext(filename)[0] + # pattern = dest + '/**/' + os.path.splitext(filename)[0] filename_base = os.path.splitext(filename)[0] - filename_pattern = os.path.join(download_path, "**", filename_base) + filename_pattern = os.path.join(dest, "**", filename_base) filename_similar = glob.glob(filename_pattern, recursive=True) if filename_similar: @@ -150,7 +150,7 @@ def run_module(): search_query=dict(type='str', required=False, default=''), download_link=dict(type='str', required=False, default=''), download_filename=dict(type='str', required=False, default=''), - download_path=dict(type='str', required=True), + dest=dict(type='str', required=True), dry_run=dict(type='bool', required=False, default=False), deduplicate=dict(type='str', required=False, default=''), search_alternatives=dict(type='bool', required=False, default=False) @@ -174,7 +174,7 @@ def run_module(): else: query = None - download_path = module.params['download_path'] + dest = module.params['dest'] download_link= module.params.get('download_link') download_filename= module.params.get('download_filename') dry_run = module.params.get('dry_run') @@ -207,7 +207,7 @@ def run_module(): # Search for existing files using exact filename filename = query if query else download_filename - filename_exact = os.path.join(download_path, filename) + filename_exact = os.path.join(dest, filename) result['filename'] = filename if os.path.exists(filename_exact): @@ -216,7 +216,7 @@ def run_module(): module.exit_json(**result) else: # Exact file not found, search for similar files with pattern - filename_similar_exists, filename_similar_names = _check_similar_files(download_path, filename) + filename_similar_exists, filename_similar_names = _check_similar_files(dest, filename) if filename_similar_exists and not (query and search_alternatives): result['skipped'] = True result['msg'] = f"Similar file(s) already exist: {', '.join(filename_similar_names)}" @@ -235,14 +235,14 @@ def run_module(): result['filename'] = download_filename result['alternative'] = True - filename_alternative_exact = os.path.join(download_path, download_filename) + filename_alternative_exact = os.path.join(dest, download_filename) if os.path.exists(filename_alternative_exact): result['skipped'] = True result['msg'] = f"Alternative file already exists: {download_filename} - original file {query} is not available to download" module.exit_json(**result) else: # Exact file not found, search for similar files with pattern - filename_similar_exists, filename_similar_names = _check_similar_files(download_path, download_filename) + filename_similar_exists, filename_similar_names = _check_similar_files(dest, download_filename) if filename_similar_exists: result['skipped'] = True result['msg'] = f"Similar alternative file(s) already exist: {', '.join(filename_similar_names)}" @@ -252,13 +252,13 @@ def run_module(): elif filename != download_filename and not alternative_found: result['filename'] = download_filename - if os.path.exists(os.path.join(download_path, download_filename)): + if os.path.exists(os.path.join(dest, download_filename)): result['skipped'] = True result['msg'] = f"File already exists with correct name: {download_filename}" module.exit_json(**result) else: # Exact file not found, search for similar files with pattern - filename_similar_exists, filename_similar_names = _check_similar_files(download_path, download_filename) + filename_similar_exists, filename_similar_names = _check_similar_files(dest, download_filename) if filename_similar_exists: result['skipped'] = True result['msg'] = f"Similar file(s) already exist for correct name {download_filename}: {', '.join(filename_similar_names)}" @@ -281,7 +281,7 @@ def run_module(): else: module.fail_json(msg="Download link {} is not available".format(download_link)) - download_software(download_link, download_filename, download_path) + download_software(download_link, download_filename, dest) # Update final results json result['changed'] = True diff --git a/roles/.gitkeep b/roles/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/roles/sap_software_download/tasks/download_files.yml b/roles/sap_software_download/tasks/download_files.yml index f209caf..177e76f 100644 --- a/roles/sap_software_download/tasks/download_files.yml +++ b/roles/sap_software_download/tasks/download_files.yml @@ -6,7 +6,7 @@ suser_id: "{{ sap_software_download_suser_id }}" suser_password: "{{ sap_software_download_suser_password }}" search_query: "{{ item }}" - download_path: "{{ sap_software_download_directory }}" + dest: "{{ sap_software_download_directory }}" search_alternatives: "{{ sap_software_download_find_alternatives | d(true) }}" deduplicate: "{{ sap_software_download_deduplicate | d('') }}" # Loop condition acts as when conditional @@ -30,7 +30,7 @@ suser_id: "{{ sap_software_download_suser_id }}" suser_password: "{{ sap_software_download_suser_password }}" search_query: "{{ item }}" - download_path: "{{ sap_software_download_directory }}" + dest: "{{ sap_software_download_directory }}" search_alternatives: "{{ sap_software_download_find_alternatives | d(true) }}" deduplicate: "{{ sap_software_download_deduplicate | d('') }}" # Loop condition acts as when conditional diff --git a/roles/sap_software_download/tasks/download_plan.yml b/roles/sap_software_download/tasks/download_plan.yml index a301294..b89af1f 100644 --- a/roles/sap_software_download/tasks/download_plan.yml +++ b/roles/sap_software_download/tasks/download_plan.yml @@ -7,7 +7,7 @@ suser_password: "{{ sap_software_download_suser_password }}" download_link: "{{ item.DirectLink }}" download_filename: "{{ item.Filename }}" - download_path: "{{ sap_software_download_directory }}" + dest: "{{ sap_software_download_directory }}" # Loop condition acts as when conditional loop: "{{ __sap_software_download_mp_transaction_results.download_basket if sap_software_download_use_venv | d(true) else [] }}" loop_control: @@ -30,7 +30,7 @@ suser_password: "{{ sap_software_download_suser_password }}" download_link: "{{ item.DirectLink }}" download_filename: "{{ item.Filename }}" - download_path: "{{ sap_software_download_directory }}" + dest: "{{ sap_software_download_directory }}" # Loop condition acts as when conditional loop: "{{ __sap_software_download_mp_transaction_results.download_basket if not sap_software_download_use_venv | d(true) else [] }}" loop_control: diff --git a/roles/sap_software_download/tasks/pre_steps/03_validate_credentials.yml b/roles/sap_software_download/tasks/pre_steps/03_validate_credentials.yml index 0769386..8154b72 100644 --- a/roles/sap_software_download/tasks/pre_steps/03_validate_credentials.yml +++ b/roles/sap_software_download/tasks/pre_steps/03_validate_credentials.yml @@ -8,7 +8,7 @@ suser_id: "{{ sap_software_download_suser_id }}" suser_password: "{{ sap_software_download_suser_password }}" search_query: "SAPCAR" - download_path: "{{ sap_software_download_directory }}" + dest: "{{ sap_software_download_directory }}" search_alternatives: true deduplicate: "last" dry_run: true @@ -29,7 +29,7 @@ suser_id: "{{ sap_software_download_suser_id }}" suser_password: "{{ sap_software_download_suser_password }}" search_query: "SAPCAR" - download_path: "{{ sap_software_download_directory }}" + dest: "{{ sap_software_download_directory }}" search_alternatives: true deduplicate: "last" dry_run: true diff --git a/roles/sap_software_download/tasks/pre_steps/05_validate_relations.yml b/roles/sap_software_download/tasks/pre_steps/05_validate_relations.yml index 5dc195e..39c9687 100644 --- a/roles/sap_software_download/tasks/pre_steps/05_validate_relations.yml +++ b/roles/sap_software_download/tasks/pre_steps/05_validate_relations.yml @@ -10,7 +10,7 @@ suser_id: "{{ sap_software_download_suser_id }}" suser_password: "{{ sap_software_download_suser_password }}" search_query: "{{ item }}" - download_path: "{{ sap_software_download_directory }}" + dest: "{{ sap_software_download_directory }}" search_alternatives: "{{ sap_software_download_find_alternatives | d(true) }}" deduplicate: "{{ sap_software_download_deduplicate | d('') }}" dry_run: true @@ -35,7 +35,7 @@ suser_id: "{{ sap_software_download_suser_id }}" suser_password: "{{ sap_software_download_suser_password }}" search_query: "{{ item }}" - download_path: "{{ sap_software_download_directory }}" + dest: "{{ sap_software_download_directory }}" search_alternatives: "{{ sap_software_download_find_alternatives | d(true) }}" deduplicate: "{{ sap_software_download_deduplicate | d('') }}" dry_run: true From 56f42ba5b15c9ae0da4e4fb221d03a9bbfae4be1 Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Thu, 3 Apr 2025 15:01:30 +0200 Subject: [PATCH 22/26] update redhat variable files --- roles/sap_software_download/vars/RedHat_8.yml | 21 +++++++++++++++++++ .../vars/{RedHat.yml => RedHat_9.yml} | 1 - 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 roles/sap_software_download/vars/RedHat_8.yml rename roles/sap_software_download/vars/{RedHat.yml => RedHat_9.yml} (95%) diff --git a/roles/sap_software_download/vars/RedHat_8.yml b/roles/sap_software_download/vars/RedHat_8.yml new file mode 100644 index 0000000..453b5ce --- /dev/null +++ b/roles/sap_software_download/vars/RedHat_8.yml @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: Apache-2.0 +--- + +# Variables specific to following versions: +# - Red Hat Linux Enterprise Server 8 + +# Set which Python version will be used on destination node. +# This is python executable name, which can differ from python package name. +__sap_software_download_python_interpreter: 'python3.9' + +# Set which Python package will be installed on destination node. +__sap_software_download_python_package: 'python39' + +# The list of required Python Modules in packages +# This is required in order to avoid externally-managed-environment error. +__sap_software_download_python_module_packages: + - python39-wheel + - python39-urllib3 + - python39-requests + - python39-beautifulsoup4 + - python39-lxml diff --git a/roles/sap_software_download/vars/RedHat.yml b/roles/sap_software_download/vars/RedHat_9.yml similarity index 95% rename from roles/sap_software_download/vars/RedHat.yml rename to roles/sap_software_download/vars/RedHat_9.yml index 6f8dc21..f472adf 100644 --- a/roles/sap_software_download/vars/RedHat.yml +++ b/roles/sap_software_download/vars/RedHat_9.yml @@ -2,7 +2,6 @@ --- # Variables specific to following versions: -# - Red Hat Linux Enterprise Server 8 # - Red Hat Linux Enterprise Server 9 # Set which Python version will be used on destination node. From 08f9a034c88c510cfa6cc51a46a2a05a14f8cfd2 Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Fri, 4 Apr 2025 12:50:45 +0200 Subject: [PATCH 23/26] fix: adjusted documentation after review --- CHANGELOG.rst | 2 +- README.md | 2 +- docs/DEVELOPER_NOTES.md | 151 ++++++++++++++++++ docs/module_maintenance_planner_files.md | 4 +- ..._maintenance_planner_stack_xml_download.md | 4 +- docs/module_software_center_download.md | 4 +- galaxy.yml | 2 +- 7 files changed, 160 insertions(+), 9 deletions(-) create mode 100644 docs/DEVELOPER_NOTES.md diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e203b4b..06a1892 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -14,9 +14,9 @@ Enhancements to Modules and introduction of new Ansible Role. Minor Changes ------------- +- sap_software_download: New Ansible Role with enhanced logic for downloading software using Ansible Module software_center_download (https://github.com/sap-linuxlab/community.sap_launchpad/pull/32) - software_center_download: Add option to search for latest packages (https://github.com/sap-linuxlab/community.sap_launchpad/pull/28) - maintenance_planner modules: Add option to use Display ID instead of name (https://github.com/sap-linuxlab/community.sap_launchpad/pull/31) -- sap_software_download: New role for downloading software (https://github.com/sap-linuxlab/community.sap_launchpad/pull/32) - Collection Readme update and preparation for 1.2.0 release (https://github.com/sap-linuxlab/community.sap_launchpad/pull/34) Bugfixes diff --git a/README.md b/README.md index 03d48fa..6686919 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ## Description -This Ansible Collection executes download of SAP Software. +This Ansible Collection provides roles and modules to automate interaction with SAP Launchpad API, primarily focusing on downloading software and files from the SAP Software Download Center and Maintenance Planner. Included role and modules cover range of options: - Preparation of environment before download. diff --git a/docs/DEVELOPER_NOTES.md b/docs/DEVELOPER_NOTES.md new file mode 100644 index 0000000..2949687 --- /dev/null +++ b/docs/DEVELOPER_NOTES.md @@ -0,0 +1,151 @@ +# Developer notes for community.sap_launchpad Ansible Collection + +This document contains details for maintaining Ansible Collection. + +## Dependencies for all modules +Modules require the following Python modules to be installed on the target node (the machine where SAP software will be downloaded): + +- wheel +- urllib3 +- requests +- beautifulsoup4 +- lxml + +### Installation of dependencies using role `sap_software_download` +Ansible Role `sap_software_download` installs all required dependencies as part of `02_prepare_python_environment.yml` task file. + +### Installation of dependencies with Python Virtual Environment (venv) +It is recommended to install dependencies in venv that can be removed after execution is completed. +```yaml +- name: Example play to install prerequisites with Python Virtual Environment + hosts: all + tasks: + - name: Create temporary directory for Python Virtual Environment + ansible.builtin.tempfile: + state: directory + suffix: __sap_software_download_venv + register: __sap_software_download_venv + + - name: Install Python and Python package manager pip + ansible.builtin.package: + name: + - python311 + - python311-pip + state: present + + - name: Install Python modules to Python Virtual Environment + ansible.builtin.pip: + name: + - wheel + - urllib3 + - requests + - beautifulsoup4 + - lxml + virtualenv: "{{ __sap_software_download_venv.path }}" + virtualenv_command: "python3.11 -m venv" + + - name: Remove temporary Python Virtual Environment + ansible.builtin.file: + path: "{{ __sap_software_download_venv.path }}" + state: absent +``` + +### Installation of dependencies with Python system default +```yaml +- name: Example play to install prerequisites with Python system default + hosts: all + tasks: + - name: Install Python and Python package manager pip + ansible.builtin.package: + name: + - python31 + - python311-pip + state: present + + - name: Install Python modules to Python system default + ansible.builtin.pip: + name: + - wheel + - urllib3 + - requests + - beautifulsoup4 + - lxml +``` + +## Additional execution methods +### Execution of Ansible Playbook, with in-line Ansible Inventory set as localhost + +```shell +# Install from local source directory for Ansible 2.11+ +ansible-galaxy collection install community.sap_launchpad + +# Workaround install from local source directory for Ansible 2.9.x +# mv ./community.sap_launchpad ~/.ansible/collections/ansible_collections/community + +# Run Ansible Collection on localhost +ansible-playbook --timeout 60 ./community.sap_launchpad/playbooks/sample-download-install-media.yml --inventory "localhost," --connection=local +``` + +### Execution of Ansible Playbook, with in-line Ansible Inventory of target/remote hosts + +```shell +# Install from local source directory for Ansible 2.11+ +ansible-galaxy collection install ./community.sap_launchpad + +# Workaround install from local source directory for Ansible 2.9.x +# mv ./community.sap_launchpad ~/.ansible/collections/ansible_collections/community + +# SSH Connection details +bastion_private_key_file="$PWD/bastion_rsa" +bastion_host="169.0.40.4" +bastion_port="50222" +bastion_user="bastionuser" + +target_private_key_file="$PWD/vs_rsa" +target_host="10.0.50.5" +target_user="root" + +# Run Ansible Collection to target/remote hosts via Proxy/Bastion +ansible-playbook --timeout 60 ./sample-playbook.yml \ +--connection 'ssh' --user "$target_user" --inventory "$target_host," --private-key "$target_private_key_file" \ +--ssh-extra-args="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ProxyCommand='ssh -W %h:%p $bastion_user@$bastion_host -p $bastion_port -i $bastion_private_key_file -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null'" +``` + +## Execution of Python Modules directly +### Setup local Python environment +```shell +# Change directory to Python scripts source +cd ./plugins + +# Create isolated Python (protect system Python) +pyenv install 3.9.6 +pyenv virtualenv 3.9.6 sap_launchpad +pyenv activate sap_launchpad + +# Install Python Modules to current Python environment +pip3 install beautifulsoup4 lxml requests + +# Run Python, import Python Modules and run Python Functions +python3 +``` + +### Execute Python Functions +```python +>>> from module_utils.sap_id_sso import sap_sso_login +>>> from module_utils.sap_launchpad_software_center_download_runner import * +>>> +>>> # Debug +>>> # from module_utils.sap_api_common import debug_https +>>> # debug_https() +>>> +>>> ## Perform API login requests to SAP Support +>>> username='S0000000' +>>> password='password' +>>> sap_sso_login(username, password) +>>> ## Perform API activity requests to SAP Support (e.g. software search without deduplication, and download software) +>>> query_result = search_software_filename("HCMT_057_0-80003261.SAR",'') +>>> download_software(*query_result, output_dir='/tmp') +... +>>> ## API responses from SAP Support +>>> exit() +``` diff --git a/docs/module_maintenance_planner_files.md b/docs/module_maintenance_planner_files.md index 080c612..b1340af 100644 --- a/docs/module_maintenance_planner_files.md +++ b/docs/module_maintenance_planner_files.md @@ -69,7 +69,7 @@ Prerequisite preparation using Python 3.11 Virtual Environment `/tmp/python_venv --- - name: Example play to install prerequisites for sap_launchpad hosts: all - pre_tasks: + tasks: - name: Install Python and Python package manager pip ansible.builtin.package: name: @@ -94,7 +94,7 @@ Prerequisite preparation using Python 3.11 system default
--- - name: Example play to install prerequisites for sap_launchpad hosts: all - pre_tasks: + tasks: - name: Install Python and Python package manager pip ansible.builtin.package: name: diff --git a/docs/module_maintenance_planner_stack_xml_download.md b/docs/module_maintenance_planner_stack_xml_download.md index 01fd249..c920652 100644 --- a/docs/module_maintenance_planner_stack_xml_download.md +++ b/docs/module_maintenance_planner_stack_xml_download.md @@ -60,7 +60,7 @@ Prerequisite preparation using Python 3.11 Virtual Environment `/tmp/python_venv --- - name: Example play to install prerequisites for sap_launchpad hosts: all - pre_tasks: + tasks: - name: Install Python and Python package manager pip ansible.builtin.package: name: @@ -85,7 +85,7 @@ Prerequisite preparation using Python 3.11 system default
--- - name: Example play to install prerequisites for sap_launchpad hosts: all - pre_tasks: + tasks: - name: Install Python and Python package manager pip ansible.builtin.package: name: diff --git a/docs/module_software_center_download.md b/docs/module_software_center_download.md index e4a8efa..84254a1 100644 --- a/docs/module_software_center_download.md +++ b/docs/module_software_center_download.md @@ -98,7 +98,7 @@ Prerequisite preparation using Python 3.11 Virtual Environment `/tmp/python_venv --- - name: Example play to install prerequisites for sap_launchpad hosts: all - pre_tasks: + tasks: - name: Install Python and Python package manager pip ansible.builtin.package: name: @@ -123,7 +123,7 @@ Prerequisite preparation using Python 3.11 system default
--- - name: Example play to install prerequisites for sap_launchpad hosts: all - pre_tasks: + tasks: - name: Install Python and Python package manager pip ansible.builtin.package: name: diff --git a/galaxy.yml b/galaxy.yml index a426fc3..02fd643 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -22,7 +22,7 @@ authors: ### OPTIONAL but strongly recommended # A short summary description of the collection -description: Collection of Ansible Modules and Roles for downloading SAP Software +description: Collection for automation of SAP Launchpad API, such as SAP Software downloads # Either a single license or a list of licenses for content inside of a collection. Ansible Galaxy currently only # accepts L(SPDX,https://spdx.org/licenses/) licenses. This key is mutually exclusive with 'license_file' From 5f70b2c98adff6145a1b1cfc31f4b12986ef670b Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Fri, 4 Apr 2025 13:18:44 +0200 Subject: [PATCH 24/26] feat: Download stack XML option --- roles/sap_software_download/README.md | 7 +++ roles/sap_software_download/defaults/main.yml | 4 ++ .../meta/argument_spec.yml | 8 +++ .../tasks/download_stack.yml | 49 +++++++++++++++++++ roles/sap_software_download/tasks/main.yml | 7 +++ 5 files changed, 75 insertions(+) create mode 100644 roles/sap_software_download/tasks/download_stack.yml diff --git a/roles/sap_software_download/README.md b/roles/sap_software_download/README.md index 93fdd7b..e26dbe0 100644 --- a/roles/sap_software_download/README.md +++ b/roles/sap_software_download/README.md @@ -256,6 +256,13 @@ A list of SAP software file names to download.
The name or display ID of a transaction from the SAP Maintenance Planner.
If provided, the role will download all files associated with this Maintenance Plan transaction.
+### sap_software_download_mp_stack_xml +- _Type:_ `boolean`
+- _Default:_ `true`
+ +Enables download of Maintenance Plan Stack XML file together with files.
+If set to `false`, Stack XML file will not be downloaded.
+ ### sap_software_download_find_alternatives - _Type:_ `boolean`
- _Default:_ `true`
diff --git a/roles/sap_software_download/defaults/main.yml b/roles/sap_software_download/defaults/main.yml index 50ac01f..d4a209f 100644 --- a/roles/sap_software_download/defaults/main.yml +++ b/roles/sap_software_download/defaults/main.yml @@ -43,6 +43,10 @@ sap_software_download_files: [] # If provided, the role will download all files associated with this Maintenance Plan transaction. sap_software_download_mp_transaction: '' +# Enables download of Maintenance Plan Stack XML file together with files. +# If set to `false`, Stack XML file will not be downloaded. +sap_software_download_mp_stack_xml: true + # Enables searching for alternative files if the requested file is not found. # Only applies to files specified in `sap_software_download_files`. # If set to `false`, the role will not search for alternatives. diff --git a/roles/sap_software_download/meta/argument_spec.yml b/roles/sap_software_download/meta/argument_spec.yml index 6a64e3e..f62f9da 100644 --- a/roles/sap_software_download/meta/argument_spec.yml +++ b/roles/sap_software_download/meta/argument_spec.yml @@ -82,6 +82,14 @@ argument_specs: - The name or display ID of a transaction from the SAP Maintenance Planner. - If provided, the role will download all files associated with this Maintenance Plan transaction. + sap_software_download_mp_stack_xml: + type: bool + required: false + default: true + description: + - Enables download of Maintenance Plan Stack XML file together with files. + - If set to `false`, Stack XML file will not be downloaded. + sap_software_download_find_alternatives: type: bool required: true diff --git a/roles/sap_software_download/tasks/download_stack.yml b/roles/sap_software_download/tasks/download_stack.yml new file mode 100644 index 0000000..c7d6ff2 --- /dev/null +++ b/roles/sap_software_download/tasks/download_stack.yml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: Apache-2.0 +--- + +- name: Download - Maintenance Plan Stack XML - Get file with Python venv + when: sap_software_download_use_venv | d(true) + community.sap_launchpad.maintenance_planner_stack_xml_download: + suser_id: "{{ sap_software_download_suser_id }}" + suser_password: "{{ sap_software_download_suser_password }}" + transaction_name: "{{ sap_software_download_mp_transaction }}" + dest: "{{ sap_software_download_directory }}" + register: __sap_software_download_stack_results_venv + retries: 1 + environment: + PATH: "{{ __sap_software_download_venv.path }}/bin:{{ ansible_env.PATH }}" + PYTHONPATH: "{{ __sap_software_download_venv.path }}/lib/{{ sap_software_download_python_interpreter }}/site-packages" + VIRTUAL_ENV: "{{ __sap_software_download_venv.path }}" + vars: + ansible_python_interpreter: "{{ __sap_software_download_venv.path ~ '/bin/' ~ sap_software_download_python_interpreter }}" + ignore_errors: true # Errors are ignored and validated afterwards + + +- name: Download - Maintenance Plan Stack XML - Get file with Python system default + when: not sap_software_download_use_venv | d(true) + community.sap_launchpad.maintenance_planner_stack_xml_download: + suser_id: "{{ sap_software_download_suser_id }}" + suser_password: "{{ sap_software_download_suser_password }}" + transaction_name: "{{ sap_software_download_mp_transaction }}" + dest: "{{ sap_software_download_directory }}" + register: __sap_software_download_stack_results_default + retries: 1 + vars: + ansible_python_interpreter: "{{ '/usr/bin/' ~ sap_software_download_python_interpreter }}" + ignore_errors: true # Errors are ignored and validated afterwards + + +- name: Download - Maintenance Plan Stack XML - Set fact with maintenance_planner_stack_xml_download output + ansible.builtin.set_fact: + __sap_software_download_stack_results: "{{ __sap_software_download_stack_results_venv + if sap_software_download_use_venv | d(true) else __sap_software_download_stack_results_default }}" + +- name: Download - Maintenance Plan Stack XML - Show failed results + ansible.builtin.fail: + msg: | + Download of Stack XML failed. + Either ensure correct value in `sap_software_download_mp_transaction` + or ignore this error with `sap_software_download_ignore_plan_not_found` set to `true`. + when: + - not sap_software_download_ignore_plan_not_found | d(false) + - __sap_software_download_stack_results.failed diff --git a/roles/sap_software_download/tasks/main.yml b/roles/sap_software_download/tasks/main.yml index 299dcf5..4763943 100644 --- a/roles/sap_software_download/tasks/main.yml +++ b/roles/sap_software_download/tasks/main.yml @@ -29,6 +29,13 @@ - sap_software_download_validate_relationships +- name: SAP Software Download - Download - Stack XML {{ sap_software_download_mp_transaction | d('') }} + ansible.builtin.include_tasks: + file: download_stack.yml + when: + - sap_software_download_mp_transaction | length > 0 + - sap_software_download_mp_stack_xml | d(true) + - name: SAP Software Download - Download - Maintenance Plan {{ sap_software_download_mp_transaction | d('') }} ansible.builtin.include_tasks: file: download_plan.yml From 1ce5a9c01791a2734b109ead5d524449b256bf2a Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Fri, 4 Apr 2025 13:30:07 +0200 Subject: [PATCH 25/26] Update readme and changelog --- CHANGELOG.rst | 1 + README.md | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 06a1892..137fc0d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -15,6 +15,7 @@ Enhancements to Modules and introduction of new Ansible Role. Minor Changes ------------- - sap_software_download: New Ansible Role with enhanced logic for downloading software using Ansible Module software_center_download (https://github.com/sap-linuxlab/community.sap_launchpad/pull/32) +- sap_software_download: Download stack XML option (https://github.com/sap-linuxlab/community.sap_launchpad/pull/35) - software_center_download: Add option to search for latest packages (https://github.com/sap-linuxlab/community.sap_launchpad/pull/28) - maintenance_planner modules: Add option to use Display ID instead of name (https://github.com/sap-linuxlab/community.sap_launchpad/pull/31) - Collection Readme update and preparation for 1.2.0 release (https://github.com/sap-linuxlab/community.sap_launchpad/pull/34) diff --git a/README.md b/README.md index 6686919..11d1a40 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ Included role and modules cover range of options: - Download of alternative SAP Software files if specific was not available. - Download of SAP Software files from existing Maintenance Plan transaction. - Download of Stack file from existing Maintenance Plan transaction. +- Register Systems and License Keys +- Query registered Systems ## Requirements From b250714c80bac882560df4174176446686adba97 Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Fri, 4 Apr 2025 13:50:21 +0200 Subject: [PATCH 26/26] doc: Update changelog for release --- changelogs/changelog.yaml | 50 +++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml index af771c1..d0abede 100644 --- a/changelogs/changelog.yaml +++ b/changelogs/changelog.yaml @@ -1,33 +1,43 @@ ancestor: null releases: - - 1.1.0: + 1.2.0: + release_date: '2025-04-04' changes: - release_summary: '| Release Date: 2023-11-28 - - | Community contribution Ansible Module systems_info to query registered systems in me.sap.com - - | Community contribution Ansible Module license_keys to create systems and license keys on me.sap.com/licensekey + release_summary: Enhancements to Modules and introduction of new Ansible Role + minor_changes: + - sap_software_download - New Ansible Role with enhanced logic for downloading software using Ansible Module software_center_download (https://github.com/sap-linuxlab/community.sap_launchpad/pull/32) + - sap_software_download - Download stack XML option (https://github.com/sap-linuxlab/community.sap_launchpad/pull/35) + - software_center_download - Add option to search for latest packages (https://github.com/sap-linuxlab/community.sap_launchpad/pull/28) + - maintenance_planner modules - Add option to use Display ID instead of name (https://github.com/sap-linuxlab/community.sap_launchpad/pull/31) + - Collection Readme update and preparation for 1.2.0 release (https://github.com/sap-linuxlab/community.sap_launchpad/pull/34) + bugfixes: + - fix - cache gigya sdk build number (https://github.com/sap-linuxlab/community.sap_launchpad/pull/33) + + 1.1.1: + release_date: '2024-03-18' + changes: + release_summary: Various bug fixes + bugfixes: + - Append logic for Account Temporarily Locked Out + - Fix errors in the example file - ' + 1.1.0: release_date: '2023-11-28' - - 1.0.1: changes: - release_summary: '| Release Date: 2023-09-14 + release_summary: Community contribution with new Ansible Modules systems_info and license_keys + minor_changes: + - Create/update systems and license keys (https://github.com/sap-linuxlab/community.sap_launchpad/pull/16) - | Fix for handling old password prompt - - ' + 1.0.1: release_date: '2023-09-14' + changes: + release_summary: Various bug fixes + bugfixes: + - Fix for handling old password prompt 1.0.0: + release_date: '2023-08-22' changes: - release_summary: '| Release Date: 2023-08-22 - - | Initial Release on Galaxy - - ' + release_summary: Initial Release on Galaxy fragments: - v1.0.0_summary.yaml - release_date: '2023-08-22'