mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
* add sap task list execute
* Apply suggestions from code review
Co-authored-by: Felix Fontein <felix@fontein.de>
* remove json out
* Apply suggestions from code review
Co-authored-by: Felix Fontein <felix@fontein.de>
* change logic
Co-authored-by: Rainer Leber <rainer.leber@sva.de>
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 1705335ba7
)
Co-authored-by: rainerleber <39616583+rainerleber@users.noreply.github.com>
This commit is contained in:
parent
e6323433ff
commit
0feb38f2b1
4 changed files with 433 additions and 0 deletions
2
.github/BOTMETA.yml
vendored
2
.github/BOTMETA.yml
vendored
|
@ -1058,6 +1058,8 @@ files:
|
|||
ignore: ryansb
|
||||
$modules/system/runit.py:
|
||||
maintainers: jsumners
|
||||
$modules/system/sap_task_list_execute:
|
||||
maintainers: rainerleber
|
||||
$modules/system/sefcontext.py:
|
||||
maintainers: dagwieers
|
||||
$modules/system/selinux_permissive.py:
|
||||
|
|
1
plugins/modules/sap_task_list_execute.py
Symbolic link
1
plugins/modules/sap_task_list_execute.py
Symbolic link
|
@ -0,0 +1 @@
|
|||
system/sap_task_list_execute.py
|
341
plugins/modules/system/sap_task_list_execute.py
Normal file
341
plugins/modules/system/sap_task_list_execute.py
Normal file
|
@ -0,0 +1,341 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2021, Rainer Leber <rainerleber@gmail.com>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
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
|
||||
from ansible.module_utils.json_utils import json
|
||||
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
|
||||
try:
|
||||
import xmltodict
|
||||
except ImportError:
|
||||
HAS_XMLTODICT_LIBRARY = False
|
||||
XMLTODICT_LIBRARY_IMPORT_ERROR = traceback.format_exc()
|
||||
else:
|
||||
HAS_XMLTODICT_LIBRARY = True
|
||||
|
||||
|
||||
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()
|
|
@ -0,0 +1,89 @@
|
|||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
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.system 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.')
|
Loading…
Reference in a new issue