From b1094d840ffc2b20a77904fd1bd5e2537047ce5a Mon Sep 17 00:00:00 2001 From: Rainer Leber <39616583+rainerleber@users.noreply.github.com> Date: Sun, 27 Nov 2022 13:59:29 +0100 Subject: [PATCH] Redirect and Remove sap modules (#5592) * redirect and remove sap modules * remove botmeta redirect * add changelog fragment * revert runtime.yml changes and add new entries * Update meta/runtime.yml Co-authored-by: Felix Fontein * Update changelogs/fragments/5592-redirect-remove-sap-modules.yml Co-authored-by: Felix Fontein * Update changelogs/fragments/5592-redirect-remove-sap-modules.yml Co-authored-by: Felix Fontein * Update 5592-redirect-remove-sap-modules.yml Fix indentation * Fix RST syntax. * Update meta/runtime.yml Co-authored-by: Felix Fontein * Update meta/runtime.yml Co-authored-by: Felix Fontein Co-authored-by: Felix Fontein --- .github/BOTMETA.yml | 6 - .../5592-redirect-remove-sap-modules.yml | 13 + meta/runtime.yml | 6 + plugins/modules/hana_query.py | 214 ----------- plugins/modules/sap_task_list_execute.py | 343 ------------------ plugins/modules/sapcar_extract.py | 221 ----------- tests/unit/plugins/modules/test_hana_query.py | 103 ------ .../modules/test_sap_task_list_execute.py | 91 ----- .../plugins/modules/test_sapcar_extract.py | 54 --- 9 files changed, 19 insertions(+), 1032 deletions(-) create mode 100644 changelogs/fragments/5592-redirect-remove-sap-modules.yml delete mode 100644 plugins/modules/hana_query.py delete mode 100644 plugins/modules/sap_task_list_execute.py delete mode 100644 plugins/modules/sapcar_extract.py delete mode 100644 tests/unit/plugins/modules/test_hana_query.py delete mode 100644 tests/unit/plugins/modules/test_sap_task_list_execute.py delete mode 100644 tests/unit/plugins/modules/test_sapcar_extract.py diff --git a/.github/BOTMETA.yml b/.github/BOTMETA.yml index 6aacdc9cad..c8e19d0b13 100644 --- a/.github/BOTMETA.yml +++ b/.github/BOTMETA.yml @@ -527,8 +527,6 @@ files: maintainers: zimbatm $modules/gunicorn.py: maintainers: agmezr - $modules/hana_query.py: - maintainers: rainerleber $modules/haproxy.py: maintainers: ravibhure Normo $modules/heroku_collaborator.py: @@ -1073,10 +1071,6 @@ files: maintainers: nerzhul $modules/runit.py: maintainers: jsumners - $modules/sap_task_list_execute: - maintainers: rainerleber - $modules/sapcar_extract.py: - maintainers: RainerLeber $modules/say.py: maintainers: $team_ansible_core ignore: mpdehaan diff --git a/changelogs/fragments/5592-redirect-remove-sap-modules.yml b/changelogs/fragments/5592-redirect-remove-sap-modules.yml new file mode 100644 index 0000000000..0d6cc2bf39 --- /dev/null +++ b/changelogs/fragments/5592-redirect-remove-sap-modules.yml @@ -0,0 +1,13 @@ +removed_features: + - | + All ``sap`` modules have been removed from this collection. + They have been migrated to the `community.sap_libs `_ collection. + Redirections have been provided. + Following modules are affected: + - sapcar_extract + - sap_task_list_execute + - hana_query +breaking_changes: + - | + If you are not using this collection as part of Ansible, but installed (and/or upgraded) community.general manually, you need to make sure to also install ``community.sap_libs`` if you are using any of the ``sapcar_extract``, ``sap_task_list_execute``, and ``hana_query`` modules. + Without that collection installed, the redirects for these modules do not work. diff --git a/meta/runtime.yml b/meta/runtime.yml index 98a46f62dc..9d15860ac4 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -972,6 +972,8 @@ plugin_routing: warning_text: You are using an internal name to access the community.general.heroku_collaborator modules. This has never been supported or documented, and will stop working in community.general 9.0.0. + hana_query: + redirect: community.sap_libs.sap_hdbsql hetzner_failover_ip: redirect: community.hrobot.failover_ip hetzner_failover_ip_info: @@ -3389,6 +3391,10 @@ plugin_routing: warning_text: You are using an internal name to access the community.general.redfish_info modules. This has never been supported or documented, and will stop working in community.general 9.0.0. + sapcar_extract: + redirect: community.sap_libs.sapcar_extract + sap_task_list_execute: + redirect: community.sap_libs.sap_task_list_execute packaging.os.redhat_subscription: redirect: community.general.redhat_subscription deprecation: diff --git a/plugins/modules/hana_query.py b/plugins/modules/hana_query.py deleted file mode 100644 index 746b2a3f44..0000000000 --- a/plugins/modules/hana_query.py +++ /dev/null @@ -1,214 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright (c) 2021, Rainer Leber -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -DOCUMENTATION = r''' ---- -module: hana_query -short_description: Execute SQL on HANA -version_added: 3.2.0 -description: This module executes SQL statements on HANA with hdbsql. -options: - sid: - description: The system ID. - type: str - required: true - instance: - description: The instance number. - type: str - required: true - user: - description: A dedicated username. The user could be also in hdbuserstore. Defaults to C(SYSTEM). - type: str - default: SYSTEM - userstore: - description: If C(true) the user must be in hdbuserstore. - type: bool - default: false - version_added: 3.5.0 - password: - description: - - The password to connect to the database. - - "B(Note:) Since the passwords have to be passed as command line arguments, I(userstore=true) should - be used whenever possible, as command line arguments can be seen by other users - on the same machine." - type: str - autocommit: - description: Autocommit the statement. - type: bool - default: true - host: - description: The Host IP address. The port can be defined as well. - type: str - database: - description: Define the database on which to connect. - type: str - encrypted: - description: Use encrypted connection. Defaults to C(false). - type: bool - default: false - filepath: - description: - - One or more files each containing one SQL query to run. - - Must be a string or list containing strings. - type: list - elements: path - query: - description: - - SQL query to run. - - Must be a string or list containing strings. Please note that if you supply a string, it will be split by commas (C(,)) to a list. - It is better to supply a one-element list instead to avoid mangled input. - type: list - elements: str -notes: - - Does not support C(check_mode). -author: - - Rainer Leber (@rainerleber) -''' - -EXAMPLES = r''' -- name: Simple select query - community.general.hana_query: - sid: "hdb" - instance: "01" - password: "Test123" - query: "select user_name from users" - -- name: Run several queries - community.general.hana_query: - sid: "hdb" - instance: "01" - password: "Test123" - query: - - "select user_name from users;" - - select * from SYSTEM; - host: "localhost" - autocommit: false - -- name: Run several queries from file - community.general.hana_query: - sid: "hdb" - instance: "01" - password: "Test123" - filepath: - - /tmp/HANA_CPU_UtilizationPerCore_2.00.020+.txt - - /tmp/HANA.txt - host: "localhost" - -- name: Run several queries from user store - community.general.hana_query: - sid: "hdb" - instance: "01" - user: hdbstoreuser - userstore: true - query: - - "select user_name from users;" - - select * from users; - autocommit: false -''' - -RETURN = r''' -query_result: - description: List containing results of all queries executed (one sublist for every query). - returned: on success - type: list - elements: list - sample: [[{"Column": "Value1"}, {"Column": "Value2"}], [{"Column": "Value1"}, {"Column": "Value2"}]] -''' - -import csv -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import StringIO -from ansible.module_utils.common.text.converters import to_native - - -def csv_to_list(rawcsv): - reader_raw = csv.DictReader(StringIO(rawcsv)) - reader = [dict((k, v.strip()) for k, v in row.items()) for row in reader_raw] - return list(reader) - - -def main(): - module = AnsibleModule( - argument_spec=dict( - sid=dict(type='str', required=True), - instance=dict(type='str', required=True), - encrypted=dict(type='bool', default=False), - host=dict(type='str', required=False), - user=dict(type='str', default="SYSTEM"), - userstore=dict(type='bool', default=False), - password=dict(type='str', no_log=True), - database=dict(type='str', required=False), - query=dict(type='list', elements='str', required=False), - filepath=dict(type='list', elements='path', required=False), - autocommit=dict(type='bool', default=True), - ), - required_one_of=[('query', 'filepath')], - required_if=[('userstore', False, ['password'])], - supports_check_mode=False, - ) - rc, out, err, out_raw = [0, [], "", ""] - - params = module.params - - sid = (params['sid']).upper() - instance = params['instance'] - user = params['user'] - userstore = params['userstore'] - password = params['password'] - autocommit = params['autocommit'] - host = params['host'] - database = params['database'] - encrypted = params['encrypted'] - - filepath = params['filepath'] - query = params['query'] - - bin_path = "/usr/sap/{sid}/HDB{instance}/exe/hdbsql".format(sid=sid, instance=instance) - - try: - command = [module.get_bin_path(bin_path, required=True)] - except Exception as e: - module.fail_json(msg='Failed to find hdbsql at the expected path "{0}". Please check SID and instance number: "{1}"'.format(bin_path, to_native(e))) - - if encrypted is True: - command.extend(['-attemptencrypt']) - if autocommit is False: - command.extend(['-z']) - if host is not None: - command.extend(['-n', host]) - if database is not None: - command.extend(['-d', database]) - # -x Suppresses additional output, such as the number of selected rows in a result set. - if userstore: - command.extend(['-x', '-U', user]) - else: - command.extend(['-x', '-i', instance, '-u', user, '-p', password]) - - if filepath is not None: - command.extend(['-I']) - for p in filepath: - # makes a command like hdbsql -i 01 -u SYSTEM -p secret123# -I /tmp/HANA_CPU_UtilizationPerCore_2.00.020+.txt, - # iterates through files and append the output to var out. - query_command = command + [p] - (rc, out_raw, err) = module.run_command(query_command) - out.append(csv_to_list(out_raw)) - if query is not None: - for q in query: - # makes a command like hdbsql -i 01 -u SYSTEM -p secret123# "select user_name from users", - # iterates through multiple commands and append the output to var out. - query_command = command + [q] - (rc, out_raw, err) = module.run_command(query_command) - out.append(csv_to_list(out_raw)) - changed = True - - module.exit_json(changed=changed, rc=rc, query_result=out, stderr=err) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/sap_task_list_execute.py b/plugins/modules/sap_task_list_execute.py deleted file mode 100644 index 29936b8483..0000000000 --- a/plugins/modules/sap_task_list_execute.py +++ /dev/null @@ -1,343 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright (c) 2021, Rainer Leber -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -DOCUMENTATION = r''' ---- -module: sap_task_list_execute -short_description: Perform SAP Task list execution -version_added: "3.5.0" -description: - - The C(sap_task_list_execute) module depends on C(pyrfc) Python library (version 2.4.0 and upwards). - Depending on distribution you are using, you may need to install additional packages to - have these available. - - Tasks in the task list which requires manual activities will be confirmed automatically. - - This module will use the RFC package C(STC_TM_API). - -requirements: - - pyrfc >= 2.4.0 - - xmltodict - -options: - conn_username: - description: The required username for the SAP system. - required: true - type: str - conn_password: - description: The required password for the SAP system. - required: true - type: str - host: - description: The required host for the SAP system. Can be either an FQDN or IP Address. - required: true - type: str - sysnr: - description: - - The system number of the SAP system. - - You must quote the value to ensure retaining the leading zeros. - default: '00' - type: str - client: - description: - - The client number to connect to. - - You must quote the value to ensure retaining the leading zeros. - default: '000' - type: str - task_to_execute: - description: The task list which will be executed. - required: true - type: str - task_parameters: - description: - - The tasks and the parameters for execution. - - If the task list do not need any parameters. This could be empty. - - If only specific tasks from the task list should be executed. - The tasks even when no parameter is needed must be provided. - Alongside with the module parameter I(task_skip=true). - type: list - elements: dict - suboptions: - TASKNAME: - description: The name of the task in the task list. - type: str - required: true - FIELDNAME: - description: The name of the field of the task. - type: str - VALUE: - description: The value which have to be set. - type: raw - task_settings: - description: - - Setting for the execution of the task list. This can be the following as in TCODE SE80 described. - Check Mode C(CHECKRUN), Background Processing Active C(BATCH) (this is the default value), - Asynchronous Execution C(ASYNC), Trace Mode C(TRACE), Server Name C(BATCH_TARGET). - default: ['BATCH'] - type: list - elements: str - task_skip: - description: - - If this parameter is C(true) not defined tasks in I(task_parameters) are skipped. - - This could be the case when only certain tasks should run from the task list. - default: false - type: bool - -notes: - - Does not support C(check_mode). -author: - - Rainer Leber (@rainerleber) -''' - -EXAMPLES = r''' -# Pass in a message -- name: Test task execution - community.general.sap_task_list_execute: - conn_username: DDIC - conn_password: Passwd1234 - host: 10.1.8.10 - sysnr: '01' - client: '000' - task_to_execute: SAP_BASIS_SSL_CHECK - task_settings: batch - -- name: Pass in input parameters - community.general.sap_task_list_execute: - conn_username: DDIC - conn_password: Passwd1234 - host: 10.1.8.10 - sysnr: '00' - client: '000' - task_to_execute: SAP_BASIS_SSL_CHECK - task_parameters : - - { 'TASKNAME': 'CL_STCT_CHECK_SEC_CRYPTO', 'FIELDNAME': 'P_OPT2', 'VALUE': 'X' } - - TASKNAME: CL_STCT_CHECK_SEC_CRYPTO - FIELDNAME: P_OPT3 - VALUE: X - task_settings: batch - -# Exported environement variables. -- name: Hint if module will fail with error message like ImportError libsapnwrfc.so... - community.general.sap_task_list_execute: - conn_username: DDIC - conn_password: Passwd1234 - host: 10.1.8.10 - sysnr: '00' - client: '000' - task_to_execute: SAP_BASIS_SSL_CHECK - task_settings: batch - environment: - SAPNWRFC_HOME: /usr/local/sap/nwrfcsdk - LD_LIBRARY_PATH: /usr/local/sap/nwrfcsdk/lib -''' - -RETURN = r''' -msg: - description: A small execution description. - type: str - returned: always - sample: 'Successful' -out: - description: A complete description of the executed tasks. If this is available. - type: list - elements: dict - returned: on success - sample: [...,{ - "LOG": { - "STCTM_S_LOG": [ - { - "ACTIVITY": "U_CONFIG", - "ACTIVITY_DESCR": "Configuration changed", - "DETAILS": null, - "EXEC_ID": "20210728184903.815739", - "FIELD": null, - "ID": "STC_TASK", - "LOG_MSG_NO": "000000", - "LOG_NO": null, - "MESSAGE": "For radiobutton group ICM too many options are set; choose only one option", - "MESSAGE_V1": "ICM", - "MESSAGE_V2": null, - "MESSAGE_V3": null, - "MESSAGE_V4": null, - "NUMBER": "048", - "PARAMETER": null, - "PERIOD": "M", - "PERIOD_DESCR": "Maintenance", - "ROW": "0", - "SRC_LINE": "170", - "SRC_OBJECT": "CL_STCTM_REPORT_UI IF_STCTM_UI_TASK~SET_PARAMETERS", - "SYSTEM": null, - "TIMESTMP": "20210728184903", - "TSTPNM": "DDIC", - "TYPE": "E" - },... - ]}}] -''' - -from ansible.module_utils.basic import AnsibleModule, missing_required_lib -import traceback -try: - from pyrfc import Connection -except ImportError: - HAS_PYRFC_LIBRARY = False - PYRFC_LIBRARY_IMPORT_ERROR = traceback.format_exc() -else: - HAS_PYRFC_LIBRARY = True - PYRFC_LIBRARY_IMPORT_ERROR = None -try: - import xmltodict -except ImportError: - HAS_XMLTODICT_LIBRARY = False - XMLTODICT_LIBRARY_IMPORT_ERROR = traceback.format_exc() -else: - HAS_XMLTODICT_LIBRARY = True - XMLTODICT_LIBRARY_IMPORT_ERROR = None - - -def call_rfc_method(connection, method_name, kwargs): - # PyRFC call function - return connection.call(method_name, **kwargs) - - -def process_exec_settings(task_settings): - # processes task settings to objects - exec_settings = {} - for settings in task_settings: - temp_dict = {settings.upper(): 'X'} - for key, value in temp_dict.items(): - exec_settings[key] = value - return exec_settings - - -def xml_to_dict(xml_raw): - try: - xml_parsed = xmltodict.parse(xml_raw, dict_constructor=dict) - xml_dict = xml_parsed['asx:abap']['asx:values']['SESSION']['TASKLIST'] - except KeyError: - xml_dict = "No logs available." - return xml_dict - - -def run_module(): - - params_spec = dict( - TASKNAME=dict(type='str', required=True), - FIELDNAME=dict(type='str'), - VALUE=dict(type='raw'), - ) - - # define available arguments/parameters a user can pass to the module - module = AnsibleModule( - argument_spec=dict( - # values for connection - conn_username=dict(type='str', required=True), - conn_password=dict(type='str', required=True, no_log=True), - host=dict(type='str', required=True), - sysnr=dict(type='str', default="00"), - client=dict(type='str', default="000"), - # values for execution tasks - task_to_execute=dict(type='str', required=True), - task_parameters=dict(type='list', elements='dict', options=params_spec), - task_settings=dict(type='list', elements='str', default=['BATCH']), - task_skip=dict(type='bool', default=False), - ), - supports_check_mode=False, - ) - result = dict(changed=False, msg='', out={}) - - params = module.params - - username = params['conn_username'].upper() - password = params['conn_password'] - host = params['host'] - sysnr = params['sysnr'] - client = params['client'] - - task_parameters = params['task_parameters'] - task_to_execute = params['task_to_execute'] - task_settings = params['task_settings'] - task_skip = params['task_skip'] - - if not HAS_PYRFC_LIBRARY: - module.fail_json( - msg=missing_required_lib('pyrfc'), - exception=PYRFC_LIBRARY_IMPORT_ERROR) - - if not HAS_XMLTODICT_LIBRARY: - module.fail_json( - msg=missing_required_lib('xmltodict'), - exception=XMLTODICT_LIBRARY_IMPORT_ERROR) - - # basic RFC connection with pyrfc - try: - conn = Connection(user=username, passwd=password, ashost=host, sysnr=sysnr, client=client) - except Exception as err: - result['error'] = str(err) - result['msg'] = 'Something went wrong connecting to the SAP system.' - module.fail_json(**result) - - try: - raw_params = call_rfc_method(conn, 'STC_TM_SCENARIO_GET_PARAMETERS', - {'I_SCENARIO_ID': task_to_execute}) - except Exception as err: - result['error'] = str(err) - result['msg'] = 'The task list does not exsist.' - module.fail_json(**result) - exec_settings = process_exec_settings(task_settings) - # initialize session task - session_init = call_rfc_method(conn, 'STC_TM_SESSION_BEGIN', - {'I_SCENARIO_ID': task_to_execute, - 'I_INIT_ONLY': 'X'}) - # Confirm Tasks which requires manual activities from Task List Run - for task in raw_params['ET_PARAMETER']: - call_rfc_method(conn, 'STC_TM_TASK_CONFIRM', - {'I_SESSION_ID': session_init['E_SESSION_ID'], - 'I_TASKNAME': task['TASKNAME']}) - if task_skip: - for task in raw_params['ET_PARAMETER']: - call_rfc_method(conn, 'STC_TM_TASK_SKIP', - {'I_SESSION_ID': session_init['E_SESSION_ID'], - 'I_TASKNAME': task['TASKNAME'], 'I_SKIP_DEP_TASKS': 'X'}) - # unskip defined tasks and set parameters - if task_parameters is not None: - for task in task_parameters: - call_rfc_method(conn, 'STC_TM_TASK_UNSKIP', - {'I_SESSION_ID': session_init['E_SESSION_ID'], - 'I_TASKNAME': task['TASKNAME'], 'I_UNSKIP_DEP_TASKS': 'X'}) - - call_rfc_method(conn, 'STC_TM_SESSION_SET_PARAMETERS', - {'I_SESSION_ID': session_init['E_SESSION_ID'], - 'IT_PARAMETER': task_parameters}) - # start the task - try: - session_start = call_rfc_method(conn, 'STC_TM_SESSION_RESUME', - {'I_SESSION_ID': session_init['E_SESSION_ID'], - 'IS_EXEC_SETTINGS': exec_settings}) - except Exception as err: - result['error'] = str(err) - result['msg'] = 'Something went wrong. See error.' - module.fail_json(**result) - # get task logs because the execution may successfully but the tasks shows errors or warnings - # returned value is ABAPXML https://help.sap.com/doc/abapdocu_755_index_htm/7.55/en-US/abenabap_xslt_asxml_general.htm - session_log = call_rfc_method(conn, 'STC_TM_SESSION_GET_LOG', - {'I_SESSION_ID': session_init['E_SESSION_ID']}) - - task_list = xml_to_dict(session_log['E_LOG']) - - result['changed'] = True - result['msg'] = session_start['E_STATUS_DESCR'] - result['out'] = task_list - - module.exit_json(**result) - - -def main(): - run_module() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/sapcar_extract.py b/plugins/modules/sapcar_extract.py deleted file mode 100644 index 57ede47616..0000000000 --- a/plugins/modules/sapcar_extract.py +++ /dev/null @@ -1,221 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright (c) 2021, Rainer Leber -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: sapcar_extract -short_description: Manages SAP SAPCAR archives -version_added: "3.2.0" -description: - - Provides support for unpacking C(sar)/C(car) files with the SAPCAR binary from SAP and pulling - information back into Ansible. -options: - path: - description: The path to the SAR/CAR file. - type: path - required: true - dest: - description: - - The destination where SAPCAR extracts the SAR file. Missing folders will be created. - If this parameter is not provided it will unpack in the same folder as the SAR file. - type: path - binary_path: - description: - - The path to the SAPCAR binary, for example, C(/home/dummy/sapcar) or C(https://myserver/SAPCAR). - If this parameter is not provided the module will look in C(PATH). - type: path - signature: - description: - - If C(true) the signature will be extracted. - default: false - type: bool - security_library: - description: - - The path to the security library, for example, C(/usr/sap/hostctrl/exe/libsapcrytp.so), for signature operations. - type: path - manifest: - description: - - The name of the manifest. - default: "SIGNATURE.SMF" - type: str - remove: - description: - - If C(true) the SAR/CAR file will be removed. B(This should be used with caution!) - default: false - type: bool -author: - - Rainer Leber (@RainerLeber) -notes: - - Always returns C(changed=true) in C(check_mode). -''' - -EXAMPLES = """ -- name: Extract SAR file - community.general.sapcar_extract: - path: "~/source/hana.sar" - -- name: Extract SAR file with destination - community.general.sapcar_extract: - path: "~/source/hana.sar" - dest: "~/test/" - -- name: Extract SAR file with destination and download from webserver can be a fileshare as well - community.general.sapcar_extract: - path: "~/source/hana.sar" - dest: "~/dest/" - binary_path: "https://myserver/SAPCAR" - -- name: Extract SAR file and delete SAR after extract - community.general.sapcar_extract: - path: "~/source/hana.sar" - remove: true - -- name: Extract SAR file with manifest - community.general.sapcar_extract: - path: "~/source/hana.sar" - signature: true - -- name: Extract SAR file with manifest and rename it - community.general.sapcar_extract: - path: "~/source/hana.sar" - manifest: "MyNewSignature.SMF" - signature: true -""" - -import os -from tempfile import NamedTemporaryFile -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.urls import open_url -from ansible.module_utils.common.text.converters import to_native - - -def get_list_of_files(dir_name): - # create a list of file and directories - # names in the given directory - list_of_file = os.listdir(dir_name) - allFiles = list() - # Iterate over all the entries - for entry in list_of_file: - # Create full path - fullPath = os.path.join(dir_name, entry) - # If entry is a directory then get the list of files in this directory - if os.path.isdir(fullPath): - allFiles = allFiles + [fullPath] - allFiles = allFiles + get_list_of_files(fullPath) - else: - allFiles.append(fullPath) - return allFiles - - -def download_SAPCAR(binary_path, module): - bin_path = None - # download sapcar binary if url is provided otherwise path is returned - if binary_path is not None: - if binary_path.startswith('https://') or binary_path.startswith('http://'): - random_file = NamedTemporaryFile(delete=False) - with open_url(binary_path) as response: - with random_file as out_file: - data = response.read() - out_file.write(data) - os.chmod(out_file.name, 0o700) - bin_path = out_file.name - module.add_cleanup_file(bin_path) - else: - bin_path = binary_path - return bin_path - - -def check_if_present(command, path, dest, signature, manifest, module): - # manipuliating output from SAR file for compare with already extracted files - iter_command = [command, '-tvf', path] - sar_out = module.run_command(iter_command)[1] - sar_raw = sar_out.split("\n")[1:] - if dest[-1] != "/": - dest = dest + "/" - sar_files = [dest + x.split(" ")[-1] for x in sar_raw if x] - # remove any SIGNATURE.SMF from list because it will not unpacked if signature is false - if not signature: - sar_files = [item for item in sar_files if '.SMF' not in item] - # if signature is renamed manipulate files in list of sar file for compare. - if manifest != "SIGNATURE.SMF": - sar_files = [item for item in sar_files if '.SMF' not in item] - sar_files = sar_files + [manifest] - # get extracted files if present - files_extracted = get_list_of_files(dest) - # compare extracted files with files in sar file - present = all(elem in files_extracted for elem in sar_files) - return present - - -def main(): - module = AnsibleModule( - argument_spec=dict( - path=dict(type='path', required=True), - dest=dict(type='path'), - binary_path=dict(type='path'), - signature=dict(type='bool', default=False), - security_library=dict(type='path'), - manifest=dict(type='str', default="SIGNATURE.SMF"), - remove=dict(type='bool', default=False), - ), - supports_check_mode=True, - ) - rc, out, err = [0, "", ""] - params = module.params - check_mode = module.check_mode - - path = params['path'] - dest = params['dest'] - signature = params['signature'] - security_library = params['security_library'] - manifest = params['manifest'] - remove = params['remove'] - - bin_path = download_SAPCAR(params['binary_path'], module) - - if dest is None: - dest_head_tail = os.path.split(path) - dest = dest_head_tail[0] + '/' - else: - if not os.path.exists(dest): - os.makedirs(dest, 0o755) - - if bin_path is not None: - command = [module.get_bin_path(bin_path, required=True)] - else: - try: - command = [module.get_bin_path('sapcar', required=True)] - except Exception as e: - module.fail_json(msg='Failed to find SAPCAR at the expected path or URL "{0}". Please check whether it is available: {1}' - .format(bin_path, to_native(e))) - - present = check_if_present(command[0], path, dest, signature, manifest, module) - - if not present: - command.extend(['-xvf', path, '-R', dest]) - if security_library: - command.extend(['-L', security_library]) - if signature: - command.extend(['-manifest', manifest]) - if not check_mode: - (rc, out, err) = module.run_command(command, check_rc=True) - changed = True - else: - changed = False - out = "already unpacked" - - if remove: - os.remove(path) - - module.exit_json(changed=changed, message=rc, stdout=out, - stderr=err, command=' '.join(command)) - - -if __name__ == '__main__': - main() diff --git a/tests/unit/plugins/modules/test_hana_query.py b/tests/unit/plugins/modules/test_hana_query.py deleted file mode 100644 index db06e4cef7..0000000000 --- a/tests/unit/plugins/modules/test_hana_query.py +++ /dev/null @@ -1,103 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright (c) 2021, Rainer Leber (@rainerleber) -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -from ansible_collections.community.general.plugins.modules import hana_query -from ansible_collections.community.general.tests.unit.plugins.modules.utils import ( - AnsibleExitJson, - AnsibleFailJson, - ModuleTestCase, - set_module_args, -) -from ansible_collections.community.general.tests.unit.compat.mock import patch -from ansible.module_utils import basic - - -def get_bin_path(*args, **kwargs): - """Function to return path of hdbsql""" - return "/usr/sap/HDB/HDB01/exe/hdbsql" - - -class Testhana_query(ModuleTestCase): - """Main class for testing hana_query module.""" - - def setUp(self): - """Setup.""" - super(Testhana_query, self).setUp() - self.module = hana_query - self.mock_get_bin_path = patch.object(basic.AnsibleModule, 'get_bin_path', get_bin_path) - self.mock_get_bin_path.start() - self.addCleanup(self.mock_get_bin_path.stop) # ensure that the patching is 'undone' - - def tearDown(self): - """Teardown.""" - super(Testhana_query, self).tearDown() - - def test_without_required_parameters(self): - """Failure must occurs when all parameters are missing.""" - with self.assertRaises(AnsibleFailJson): - set_module_args({}) - self.module.main() - - def test_hana_query(self): - """Check that result is processed.""" - set_module_args({ - 'sid': "HDB", - 'instance': "01", - 'encrypted': False, - 'host': "localhost", - 'user': "SYSTEM", - 'password': "1234Qwer", - 'database': "HDB", - 'query': "SELECT * FROM users;" - }) - with patch.object(basic.AnsibleModule, 'run_command') as run_command: - run_command.return_value = 0, 'username,name\n testuser,test user \n myuser, my user \n', '' - with self.assertRaises(AnsibleExitJson) as result: - hana_query.main() - self.assertEqual(result.exception.args[0]['query_result'], [[ - {'username': 'testuser', 'name': 'test user'}, - {'username': 'myuser', 'name': 'my user'}, - ]]) - self.assertEqual(run_command.call_count, 1) - - def test_hana_userstore_query(self): - """Check that result is processed with userstore.""" - set_module_args({ - 'sid': "HDB", - 'instance': "01", - 'encrypted': False, - 'host': "localhost", - 'user': "SYSTEM", - 'userstore': True, - 'database': "HDB", - 'query': "SELECT * FROM users;" - }) - with patch.object(basic.AnsibleModule, 'run_command') as run_command: - run_command.return_value = 0, 'username,name\n testuser,test user \n myuser, my user \n', '' - with self.assertRaises(AnsibleExitJson) as result: - hana_query.main() - self.assertEqual(result.exception.args[0]['query_result'], [[ - {'username': 'testuser', 'name': 'test user'}, - {'username': 'myuser', 'name': 'my user'}, - ]]) - self.assertEqual(run_command.call_count, 1) - - def test_hana_failed_no_passwd(self): - """Check that result is failed with no password.""" - with self.assertRaises(AnsibleFailJson): - set_module_args({ - 'sid': "HDB", - 'instance': "01", - 'encrypted': False, - 'host': "localhost", - 'user': "SYSTEM", - 'database': "HDB", - 'query': "SELECT * FROM users;" - }) - self.module.main() diff --git a/tests/unit/plugins/modules/test_sap_task_list_execute.py b/tests/unit/plugins/modules/test_sap_task_list_execute.py deleted file mode 100644 index 34c97c4a80..0000000000 --- a/tests/unit/plugins/modules/test_sap_task_list_execute.py +++ /dev/null @@ -1,91 +0,0 @@ -# Copyright (c) Ansible project -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import sys -from ansible_collections.community.general.tests.unit.compat.mock import patch, MagicMock -from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args - -sys.modules['pyrfc'] = MagicMock() -sys.modules['pyrfc.Connection'] = MagicMock() -sys.modules['xmltodict'] = MagicMock() -sys.modules['xmltodict.parse'] = MagicMock() - -from ansible_collections.community.general.plugins.modules import sap_task_list_execute - - -class TestSAPRfcModule(ModuleTestCase): - - def setUp(self): - super(TestSAPRfcModule, self).setUp() - self.module = sap_task_list_execute - - def tearDown(self): - super(TestSAPRfcModule, self).tearDown() - - def define_rfc_connect(self, mocker): - return mocker.patch(self.module.call_rfc_method) - - def test_without_required_parameters(self): - """Failure must occurs when all parameters are missing""" - with self.assertRaises(AnsibleFailJson): - set_module_args({}) - self.module.main() - - def test_error_no_task_list(self): - """tests fail to exec task list""" - - set_module_args({ - "conn_username": "DDIC", - "conn_password": "Test1234", - "host": "10.1.8.9", - "task_to_execute": "SAP_BASIS_SSL_CHECK" - }) - - with patch.object(self.module, 'Connection') as conn: - conn.return_value = '' - with self.assertRaises(AnsibleFailJson) as result: - self.module.main() - self.assertEqual(result.exception.args[0]['msg'], 'The task list does not exsist.') - - def test_success(self): - """test execute task list success""" - - set_module_args({ - "conn_username": "DDIC", - "conn_password": "Test1234", - "host": "10.1.8.9", - "task_to_execute": "SAP_BASIS_SSL_CHECK" - }) - with patch.object(self.module, 'xml_to_dict') as XML: - XML.return_value = {'item': [{'TASK': {'CHECK_STATUS_DESCR': 'Check successfully', - 'STATUS_DESCR': 'Executed successfully', 'TASKNAME': 'CL_STCT_CHECK_SEC_CRYPTO', - 'LNR': '1', 'DESCRIPTION': 'Check SAP Cryptographic Library', 'DOCU_EXIST': 'X', - 'LOG_EXIST': 'X', 'ACTION_SKIP': None, 'ACTION_UNSKIP': None, 'ACTION_CONFIRM': None, - 'ACTION_MAINTAIN': None}}]} - - with self.assertRaises(AnsibleExitJson) as result: - sap_task_list_execute.main() - self.assertEqual(result.exception.args[0]['out'], {'item': [{'TASK': {'CHECK_STATUS_DESCR': 'Check successfully', - 'STATUS_DESCR': 'Executed successfully', 'TASKNAME': 'CL_STCT_CHECK_SEC_CRYPTO', - 'LNR': '1', 'DESCRIPTION': 'Check SAP Cryptographic Library', 'DOCU_EXIST': 'X', - 'LOG_EXIST': 'X', 'ACTION_SKIP': None, 'ACTION_UNSKIP': None, - 'ACTION_CONFIRM': None, 'ACTION_MAINTAIN': None}}]}) - - def test_success_no_log(self): - """test execute task list success without logs""" - - set_module_args({ - "conn_username": "DDIC", - "conn_password": "Test1234", - "host": "10.1.8.9", - "task_to_execute": "SAP_BASIS_SSL_CHECK" - }) - with patch.object(self.module, 'xml_to_dict') as XML: - XML.return_value = "No logs available." - with self.assertRaises(AnsibleExitJson) as result: - sap_task_list_execute.main() - self.assertEqual(result.exception.args[0]['out'], 'No logs available.') diff --git a/tests/unit/plugins/modules/test_sapcar_extract.py b/tests/unit/plugins/modules/test_sapcar_extract.py deleted file mode 100644 index bec9cf8862..0000000000 --- a/tests/unit/plugins/modules/test_sapcar_extract.py +++ /dev/null @@ -1,54 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright (c) 2021, Rainer Leber (@rainerleber) -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -from ansible_collections.community.general.plugins.modules import sapcar_extract -from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args -from ansible_collections.community.general.tests.unit.compat.mock import patch -from ansible.module_utils import basic - - -def get_bin_path(*args, **kwargs): - """Function to return path of SAPCAR""" - return "/tmp/sapcar" - - -class Testsapcar_extract(ModuleTestCase): - """Main class for testing sapcar_extract module.""" - - def setUp(self): - """Setup.""" - super(Testsapcar_extract, self).setUp() - self.module = sapcar_extract - self.mock_get_bin_path = patch.object(basic.AnsibleModule, 'get_bin_path', get_bin_path) - self.mock_get_bin_path.start() - self.addCleanup(self.mock_get_bin_path.stop) # ensure that the patching is 'undone' - - def tearDown(self): - """Teardown.""" - super(Testsapcar_extract, self).tearDown() - - def test_without_required_parameters(self): - """Failure must occurs when all parameters are missing.""" - with self.assertRaises(AnsibleFailJson): - set_module_args({}) - self.module.main() - - def test_sapcar_extract(self): - """Check that result is changed.""" - set_module_args({ - 'path': "/tmp/HANA_CLIENT_REV2_00_053_00_LINUX_X86_64.SAR", - 'dest': "/tmp/test2", - 'binary_path': "/tmp/sapcar" - }) - with patch.object(basic.AnsibleModule, 'run_command') as run_command: - run_command.return_value = 0, '', '' # successful execution, no output - with self.assertRaises(AnsibleExitJson) as result: - sapcar_extract.main() - self.assertTrue(result.exception.args[0]['changed']) - self.assertEqual(run_command.call_count, 1)