Skip to content

Commit abaa8d9

Browse files
authored
Merge pull request #77 from marcelmamula/sapcar
sapcar_extract: Add overwrite mode and improve exist validation
2 parents 93d8678 + 725275b commit abaa8d9

2 files changed

Lines changed: 92 additions & 24 deletions

File tree

plugins/modules/sapcar_extract.py

Lines changed: 71 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,13 @@
5656
- If C(true), the SAR/CAR file will be removed. B(This should be used with caution!)
5757
default: false
5858
type: bool
59+
overwrite:
60+
description:
61+
- If C(true), existing files will be overwritten during extraction. B(This should be used with caution!)
62+
- If C(false), the module checks if the expected file names are already present in the destination folder and only extracts if they are not found.
63+
- It does not remove files, but overwrites them if they are already present in the destination folder.
64+
default: false
65+
type: bool
5966
author:
6067
- Rainer Leber (@RainerLeber)
6168
notes:
@@ -95,6 +102,34 @@
95102
signature: true
96103
"""
97104

105+
RETURN = r'''
106+
msg:
107+
description: Status message about the extraction operation.
108+
type: str
109+
returned: always
110+
sample: "Files extracted to /tmp/test2 (overwrite mode enabled)"
111+
stdout:
112+
description: Standard output from the SAPCAR command.
113+
type: str
114+
returned: always
115+
sample: "SAPCAR: processing archive /tmp/hana.sar (version 2.01)\\nfile1\\nfile2"
116+
stderr:
117+
description: Standard error from the SAPCAR command.
118+
type: str
119+
returned: always
120+
sample: ""
121+
command:
122+
description: The full SAPCAR command that was executed.
123+
type: str
124+
returned: always
125+
sample: "/tmp/sapcar -xvf /tmp/hana.sar -R /tmp/test2"
126+
changed:
127+
description: Whether the module made changes.
128+
type: bool
129+
returned: always
130+
sample: true
131+
'''
132+
98133
import os
99134
from tempfile import NamedTemporaryFile
100135
from ansible.module_utils.basic import AnsibleModule
@@ -139,24 +174,29 @@ def download_SAPCAR(binary_path, module):
139174

140175

141176
def check_if_present(command, path, dest, signature, manifest, module):
142-
# manipulating output from SAR file for compare with already extracted files
177+
# Get list of files from sar file without extraction
143178
iter_command = [command, '-tvf', path]
144179
sar_out = module.run_command(iter_command)[1]
145180
sar_raw = sar_out.split("\n")[1:]
181+
146182
if dest[-1] != "/":
147183
dest = dest + "/"
148184
sar_files = [dest + x.split(" ")[-1] for x in sar_raw if x]
185+
149186
# remove any SIGNATURE.SMF from list because it will not unpacked if signature is false
150187
if not signature:
151188
sar_files = [item for item in sar_files if not item.endswith('.SMF')]
189+
152190
# if signature is renamed manipulate files in list of sar file for compare.
153191
if manifest != "SIGNATURE.SMF":
154192
sar_files = [item for item in sar_files if not item.endswith('.SMF')]
155193
sar_files = sar_files + [manifest]
194+
156195
# get extracted files if present
157196
files_extracted = get_list_of_files(dest)
158197
# compare extracted files with files in sar file
159198
present = all(elem in files_extracted for elem in sar_files)
199+
160200
return present
161201

162202

@@ -170,24 +210,27 @@ def main():
170210
security_library=dict(type='path'),
171211
manifest=dict(type='str', default="SIGNATURE.SMF"),
172212
remove=dict(type='bool', default=False),
213+
overwrite=dict(type='bool', default=False)
173214
),
174215
supports_check_mode=True,
175216
)
176217
rc, out, err = [0, "", ""]
177218
params = module.params
178219
check_mode = module.check_mode
179220

180-
path = params['path']
181-
dest = params['dest']
182-
signature = params['signature']
183-
security_library = params['security_library']
184-
manifest = params['manifest']
185-
remove = params['remove']
186-
187221
bin_path = download_SAPCAR(params['binary_path'], module)
188222

223+
# Check if path is present and readable
224+
if os.path.isfile(params['path']):
225+
if not os.access(params['path'], os.R_OK):
226+
module.fail_json(msg="Permission denied: File defined in the 'path' parameter is not readable: {0}".format(params['path']))
227+
else:
228+
module.fail_json(msg='File missing: File defined in the "path" parameter does not exist: {0}'.format(params['path']))
229+
230+
# Check if destination exists and it is directory, if not create it.
231+
dest = params['dest']
189232
if dest is None:
190-
dest_head_tail = os.path.split(path)
233+
dest_head_tail = os.path.split(params['path'])
191234
dest = dest_head_tail[0] + '/'
192235
else:
193236
if not os.path.exists(dest):
@@ -202,25 +245,33 @@ def main():
202245
module.fail_json(msg='Failed to find SAPCAR at the expected path or URL "{0}". Please check whether it is available: {1}'
203246
.format(bin_path, to_native(e)))
204247

205-
present = check_if_present(command[0], path, dest, signature, manifest, module)
248+
present = check_if_present(command[0], params['path'], dest, params['signature'], params['manifest'], module)
249+
250+
if not present or params['overwrite']:
251+
command.extend(['-xvf', params['path'], '-R', dest])
252+
253+
if params['security_library']:
254+
command.extend(['-L', params['security_library']])
255+
256+
if params['signature']:
257+
command.extend(['-manifest', params['manifest']])
206258

207-
if not present:
208-
command.extend(['-xvf', path, '-R', dest])
209-
if security_library:
210-
command.extend(['-L', security_library])
211-
if signature:
212-
command.extend(['-manifest', manifest])
213259
if not check_mode:
214260
(rc, out, err) = module.run_command(command, check_rc=True)
215261
changed = True
262+
msg = "Files extracted to {0}".format(dest)
263+
if params['overwrite']:
264+
msg += " (overwrite mode enabled)"
265+
216266
else:
217267
changed = False
218-
out = "already unpacked"
268+
msg = "Expected file names were found in {0}. No extraction needed.".format(dest)
269+
out = ""
219270

220-
if remove:
221-
os.remove(path)
271+
if params['remove']:
272+
os.remove(params['path'])
222273

223-
module.exit_json(changed=changed, message=rc, stdout=out,
274+
module.exit_json(changed=changed, msg=msg, stdout=out,
224275
stderr=err, command=' '.join(command))
225276

226277

tests/unit/plugins/modules/test_sapcar_extract.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,27 @@ def test_sapcar_extract(self):
4545
'dest': "/tmp/test2",
4646
'binary_path': "/tmp/sapcar"
4747
}
48-
with patch.object(basic.AnsibleModule, 'run_command') as run_command:
49-
run_command.return_value = 0, '', '' # successful execution, no output
48+
with patch('os.path.isfile', return_value=True), \
49+
patch('os.access', return_value=True), \
50+
patch('os.path.exists', return_value=True), \
51+
patch('os.listdir', return_value=[]), \
52+
patch.object(basic.AnsibleModule, 'run_command') as run_command:
53+
run_command.return_value = 0, 'file1\nfile2', ''
5054
with self.assertRaises(AnsibleExitJson) as result:
5155
with set_module_args(args):
5256
sapcar_extract.main()
53-
self.assertTrue(result.exception.args[0]['changed'])
54-
self.assertEqual(run_command.call_count, 1)
57+
self.assertTrue(result.exception.args[0]['changed'])
58+
self.assertGreaterEqual(run_command.call_count, 1)
59+
60+
def test_file_missing(self):
61+
"""Failure must occur when SAR file does not exist."""
62+
args = {
63+
'path': "/tmp/nonexistent.SAR",
64+
'dest': "/tmp/test2",
65+
'binary_path': "/tmp/sapcar"
66+
}
67+
with patch('os.path.isfile', return_value=False):
68+
with self.assertRaises(AnsibleFailJson) as result:
69+
with set_module_args(args):
70+
sapcar_extract.main()
71+
self.assertIn('File missing', result.exception.args[0]['msg'])

0 commit comments

Comments
 (0)