Skip to content

Commit cad3449

Browse files
authored
initial migration of pyrfc module to sap_libs (#12)
* initial migration * add suboptions * refactoring as discussed
1 parent 8366b79 commit cad3449

File tree

9 files changed

+314
-1
lines changed

9 files changed

+314
-1
lines changed

.github/workflows/ansible-test.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ jobs:
2222
- stable-2.11
2323
- stable-2.12
2424
- stable-2.13
25-
- devel
2625
runs-on: ubuntu-latest
2726
steps:
2827

plugins/module_utils/pyrfc_handler.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# -*- coding: utf-8 -*-
2+
3+
# Copyright: (c) 2022, Sean Freeman ,
4+
# Rainer Leber <rainerleber@gmail.com> <rainer.leber@sva.de>
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from __future__ import (absolute_import, division, print_function)
16+
__metaclass__ = type
17+
18+
from ansible.module_utils.basic import missing_required_lib
19+
20+
import traceback
21+
22+
PYRFC_LIBRARY_IMPORT_ERROR = None
23+
try:
24+
import pyrfc
25+
except ImportError:
26+
PYRFC_LIBRARY_IMPORT_ERROR = traceback.format_exc()
27+
HAS_PYRFC_LIBRARY = False
28+
else:
29+
HAS_PYRFC_LIBRARY = True
30+
31+
32+
def get_connection(module, conn_params):
33+
if not HAS_PYRFC_LIBRARY:
34+
module.fail_json(msg=missing_required_lib(
35+
"python-gitlab"), exception=PYRFC_LIBRARY_IMPORT_ERROR)
36+
37+
module.warn('Connecting ... %s' % conn_params['ashost'])
38+
if "saprouter" in conn_params:
39+
module.warn("...via SAPRouter to SAP System")
40+
elif "gwhost" in conn_params:
41+
module.warn("...via Gateway to SAP System")
42+
else:
43+
module.warn("...direct to SAP System")
44+
45+
conn = pyrfc.Connection(**conn_params)
46+
47+
module.warn("Verifying connection is open/alive: %s" % conn.alive)
48+
return conn

plugins/modules/sap_pyrfc.py

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
3+
4+
# Copyright: (c) 2022, Sean Freeman ,
5+
# Rainer Leber <rainerleber@gmail.com> <rainer.leber@sva.de>
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
from __future__ import absolute_import, division, print_function
16+
__metaclass__ = type
17+
18+
DOCUMENTATION = r'''
19+
---
20+
module: sap_pyrfc
21+
22+
short_description: This module executes rfc functions.
23+
24+
version_added: "1.2.0"
25+
26+
description:
27+
- This module will executes rfc calls on a sap system.
28+
- It is a generic approach to call rfc functions on a SAP System.
29+
- This module should be used where no module or role is provided.
30+
31+
options:
32+
function:
33+
description: The SAP RFC function to call.
34+
required: true
35+
type: str
36+
parameters:
37+
description: The parameters which are needed by the function.
38+
required: true
39+
type: dict
40+
connection:
41+
description: The required connection details.
42+
required: true
43+
type: dict
44+
suboptions:
45+
ashost:
46+
description: The required host for the SAP system. Can be either an FQDN or IP Address.
47+
type: str
48+
required: true
49+
sysid:
50+
description: The systemid of the SAP system.
51+
type: str
52+
required: false
53+
sysnr:
54+
description:
55+
- The system number of the SAP system.
56+
- You must quote the value to ensure retaining the leading zeros.
57+
type: str
58+
required: true
59+
client:
60+
description:
61+
- The client number to connect to.
62+
- You must quote the value to ensure retaining the leading zeros.
63+
type: str
64+
required: true
65+
user:
66+
description: The required username for the SAP system.
67+
type: str
68+
required: true
69+
passwd:
70+
description: The required password for the SAP system.
71+
type: str
72+
required: true
73+
lang:
74+
description: The used language to execute.
75+
type: str
76+
required: false
77+
78+
requirements:
79+
- pyrfc >= 2.4.0
80+
81+
author:
82+
- Sean Freeman (@seanfreeman)
83+
- Rainer Leber (@rainerleber)
84+
'''
85+
86+
EXAMPLES = '''
87+
- name: test the pyrfc module
88+
community.sap_libs.sap_pyrfc:
89+
function: STFC_CONNECTION
90+
parameters:
91+
REQUTEXT: "Hello SAP!"
92+
connection:
93+
ashost: s4hana.poc.cloud
94+
sysid: TDT
95+
sysnr: "01"
96+
client: "400"
97+
user: DDIC
98+
passwd: Password1
99+
lang: EN
100+
'''
101+
102+
RETURN = r'''
103+
result:
104+
description: The execution description.
105+
type: dict
106+
returned: always
107+
sample: {"ECHOTEXT": "Hello SAP!",
108+
"RESPTEXT": "SAP R/3 Rel. 756 Sysid: TST Date: 20220710 Time: 140717 Logon_Data: 000/DDIC/E"}
109+
'''
110+
111+
import traceback
112+
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
113+
from ..module_utils.pyrfc_handler import get_connection
114+
115+
try:
116+
from pyrfc import ABAPApplicationError, ABAPRuntimeError, CommunicationError, Connection, LogonError
117+
except ImportError:
118+
HAS_PYRFC_LIBRARY = False
119+
PYRFC_LIBRARY_IMPORT_ERROR = traceback.format_exc()
120+
else:
121+
HAS_PYRFC_LIBRARY = True
122+
123+
124+
def main():
125+
msg = None
126+
params_spec = dict(
127+
ashost=dict(type='str', required=True),
128+
sysid=dict(type='str', required=False),
129+
sysnr=dict(type='str', required=True),
130+
client=dict(type='str', required=True),
131+
user=dict(type='str', required=True),
132+
passwd=dict(type='str', required=True, no_log=True),
133+
lang=dict(type='str', required=False),
134+
)
135+
136+
argument_spec = dict(function=dict(required=True, type='str'),
137+
parameters=dict(required=True, type='dict'),
138+
connection=dict(
139+
required=True, type='dict', options=params_spec),
140+
)
141+
142+
module = AnsibleModule(
143+
argument_spec=argument_spec,
144+
supports_check_mode=True,
145+
)
146+
147+
function = module.params.get('function')
148+
func_params = module.params.get('parameters')
149+
conn_params = module.params.get('connection')
150+
151+
if not HAS_PYRFC_LIBRARY:
152+
module.fail_json(
153+
msg=missing_required_lib('pyrfc'),
154+
exception=PYRFC_LIBRARY_IMPORT_ERROR)
155+
156+
# Check mode
157+
if module.check_mode:
158+
msg = "function: %s; params: %s; login: %s" % (
159+
function, func_params, conn_params)
160+
module.exit_json(msg=msg, changed=True)
161+
162+
try:
163+
conn = get_connection(module, conn_params)
164+
result = conn.call(function, **func_params)
165+
except CommunicationError as err:
166+
msg = "Could not connect to server"
167+
error_msg = err.message
168+
except LogonError as err:
169+
msg = "Could not log in"
170+
error_msg = err.message
171+
except (ABAPApplicationError, ABAPRuntimeError) as err:
172+
msg = "ABAP error occurred"
173+
error_msg = err.message
174+
except Exception as err:
175+
msg = "Something went wrong."
176+
error_msg = err
177+
else:
178+
module.exit_json(changed=True, result=result)
179+
180+
if msg:
181+
module.fail_json(msg=msg, exception=error_msg)
182+
183+
184+
if __name__ == '__main__':
185+
main()

tests/sanity/ignore-2.10.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
plugins/modules/sap_pyrfc.py validate-modules:missing-gplv3-license # Licensed under Apache 2.0

tests/sanity/ignore-2.11.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
plugins/modules/sap_pyrfc.py validate-modules:missing-gplv3-license # Licensed under Apache 2.0

tests/sanity/ignore-2.12.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
plugins/modules/sap_pyrfc.py validate-modules:missing-gplv3-license # Licensed under Apache 2.0

tests/sanity/ignore-2.13.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
plugins/modules/sap_pyrfc.py validate-modules:missing-gplv3-license # Licensed under Apache 2.0

tests/sanity/ignore-2.9.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
plugins/modules/sap_pyrfc.py validate-modules:missing-gplv3-license # Licensed under Apache 2.0
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License");
2+
# you may not use this file except in compliance with the License.
3+
# You may obtain a copy of the License at
4+
# http://www.apache.org/licenses/LICENSE-2.0
5+
# Unless required by applicable law or agreed to in writing, software
6+
# distributed under the License is distributed on an "AS IS" BASIS,
7+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8+
# See the License for the specific language governing permissions and
9+
# limitations under the License.
10+
11+
from __future__ import (absolute_import, division, print_function)
12+
13+
__metaclass__ = type
14+
15+
import sys
16+
from ansible_collections.community.sap_libs.tests.unit.compat.mock import patch, MagicMock
17+
from ansible_collections.community.sap_libs.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args
18+
19+
sys.modules['pyrfc'] = MagicMock()
20+
sys.modules['pyrfc.Connection'] = MagicMock()
21+
from ansible_collections.community.sap_libs.plugins.modules import sap_pyrfc
22+
23+
24+
class TestSAPRfcModule(ModuleTestCase):
25+
26+
def setUp(self):
27+
super(TestSAPRfcModule, self).setUp()
28+
self.module = sap_pyrfc
29+
30+
def tearDown(self):
31+
super(TestSAPRfcModule, self).tearDown()
32+
33+
def test_without_required_parameters(self):
34+
"""Failure must occurs when all parameters are missing"""
35+
with self.assertRaises(AnsibleFailJson):
36+
set_module_args({})
37+
self.module.main()
38+
39+
def test_error_module_not_found(self):
40+
"""tests fail module error"""
41+
42+
set_module_args({
43+
"function": "STFC_CONNECTION",
44+
"parameters": {"REQUTEXT": "Hello SAP!"},
45+
"connection": {"ashost": "s4hana.poc.cloud",
46+
"sysnr": "01",
47+
"client": "400",
48+
"user": "DDIC",
49+
"passwd": "Password1",
50+
"lang": "EN"}
51+
})
52+
53+
with self.assertRaises(AnsibleFailJson) as result:
54+
self.module.HAS_PYRFC_LIBRARY = False
55+
self.module.PYRFC_LIBRARY_IMPORT_ERROR = 'Module not found'
56+
self.module.main()
57+
self.assertEqual(
58+
result.exception.args[0]['exception'], 'Module not found')
59+
60+
def test_success_communication(self):
61+
"""tests success"""
62+
set_module_args({
63+
"function": "STFC_CONNECTION",
64+
"parameters": {"REQUTEXT": "Hello SAP!"},
65+
"connection": {"ashost": "s4hana.poc.cloud",
66+
"sysnr": "01",
67+
"client": "400",
68+
"user": "DDIC",
69+
"passwd": "Password1",
70+
"lang": "EN"}
71+
})
72+
with patch.object(self.module, 'get_connection') as patch_call:
73+
patch_call.call.return_value = 'Patched'
74+
with self.assertRaises(AnsibleExitJson) as result:
75+
self.module.main()
76+
self.assertEqual(result.exception.args[0]['changed'], True)

0 commit comments

Comments
 (0)