Skip to content

Commit 78e1839

Browse files
Avoid failures when default dir ~/.ansible/tmp/ is not previously created and fix failures when using become in zos_job_submit (#2115)
* cherry picked fix * Updated changelog
1 parent 39f3b71 commit 78e1839

File tree

4 files changed

+91
-25
lines changed

4 files changed

+91
-25
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
bugfixes:
2+
- zos_job_submit - Previously, the use of `become` would result in a permissions error
3+
while trying to execute a job from a local file. Fix now allows a user to escalate
4+
privileges when executing a job transferred from the controller node.
5+
(https://github.com/ansible-collections/ibm_zos_core/pull/2115)
6+
- zos_copy - Previously, when trying to copy into remote and ansible's default temporary directory
7+
was not created before execution the copy task would fail. Fix now creates the temporary directory if possible.
8+
(https://github.com/ansible-collections/ibm_zos_core/pull/2115)

plugins/action/zos_copy.py

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ def run(self, tmp=None, task_vars=None):
155155
try:
156156
local_content = _write_content_to_temp_file(content)
157157
transfer_res = self._copy_to_remote(
158-
local_content, ignore_stderr=ignore_sftp_stderr
158+
local_content, ignore_stderr=ignore_sftp_stderr, task_vars=task_vars
159159
)
160160
finally:
161161
os.remove(local_content)
@@ -243,7 +243,7 @@ def run(self, tmp=None, task_vars=None):
243243

244244
display.vvv(u"ibm_zos_copy calculated size: {0}".format(os.stat(src).st_size), host=self._play_context.remote_addr)
245245
transfer_res = self._copy_to_remote(
246-
src, is_dir=is_src_dir, ignore_stderr=ignore_sftp_stderr
246+
src, is_dir=is_src_dir, ignore_stderr=ignore_sftp_stderr, task_vars=task_vars
247247
)
248248

249249
temp_path = transfer_res.get("temp_path")
@@ -291,7 +291,26 @@ def run(self, tmp=None, task_vars=None):
291291
# Remove temporary directory from remote
292292
if self.tmp_dir is not None:
293293
path = os.path.normpath(f"{self.tmp_dir}/ansible-zos-copy")
294-
self._connection.exec_command(f"rm -rf {path}*")
294+
# If another user created the temporary files, we'll need to run rm
295+
# with it too, lest we get a permissions issue.
296+
if self._connection.become:
297+
# We get the dirname from temp_path and not path = os.path.normpath(f"{self.tmp_dir}/ansible-zos-copy")
298+
# because if default is ~/.ansible/tmp/ when using become it would be similar to /root/.ansible/tmp
299+
# but the original tmp directory was resolved when user is non escalated yet. Meaning, the original
300+
# tmp directory is similar to /u/usrt001/.ansible/tmp.
301+
path = os.path.dirname(temp_path)
302+
self._connection.set_option('remote_user', self._play_context._become_user)
303+
display.vvv(
304+
u"ibm_zos_copy SSH cleanup user updated to {0}".format(self._play_context._become_user),
305+
host=self._play_context.remote_addr
306+
)
307+
rm_res = self._connection.exec_command(f"rm -rf {path}*")
308+
if self._connection.become:
309+
self._connection.set_option('remote_user', self._play_context._remote_user)
310+
display.vvv(
311+
u"ibm_zos_copy SSH cleanup user restored to {0}".format(self._play_context._remote_user),
312+
host=self._play_context.remote_addr
313+
)
295314

296315
if copy_res.get("note") and not force:
297316
result["note"] = copy_res.get("note")
@@ -319,18 +338,21 @@ def run(self, tmp=None, task_vars=None):
319338

320339
return copy_res
321340

322-
def _copy_to_remote(self, src, is_dir=False, ignore_stderr=False):
341+
def _copy_to_remote(self, src, is_dir=False, ignore_stderr=False, task_vars=None):
323342
"""Copy a file or directory to the remote z/OS system """
324343
self.tmp_dir = self._connection._shell._options.get("remote_tmp")
325-
rc, stdout, stderr = self._connection.exec_command("cd {0} && pwd".format(self.tmp_dir))
326-
if rc > 0:
327-
msg = f"Failed to resolve remote temporary directory {self.tmp_dir}. Ensure that the directory exists and user has proper access."
328-
return self._exit_action({}, msg, failed=True)
329-
self.tmp_dir = stdout.decode("utf-8").replace("\r", "").replace("\n", "")
330-
temp_path = os.path.join(self.tmp_dir, _create_temp_path_name(), os.path.basename(src))
331-
self._connection.exec_command("mkdir -p {0}".format(os.path.dirname(temp_path)))
332-
_src = src.replace("#", "\\#")
344+
temp_path = os.path.join(self.tmp_dir, _create_temp_path_name())
345+
tempfile_args = {"path": temp_path, "state": "directory", "mode": "666"}
346+
# Reverted this back to using file ansible module so ansible would handle all temporary dirs
347+
# creation with correct permissions.
348+
tempfile = self._execute_module(
349+
module_name="file", module_args=tempfile_args, task_vars=task_vars, wrap_async=self._task.async_val
350+
)
333351
_sftp_action = 'put'
352+
was_user_updated = False
353+
354+
temp_path = os.path.join(tempfile.get("path"), os.path.basename(src))
355+
_src = src.replace("#", "\\#")
334356
full_temp_path = temp_path
335357

336358
if is_dir:
@@ -370,6 +392,13 @@ def _copy_to_remote(self, src, is_dir=False, ignore_stderr=False):
370392
sftp_transfer_method), host=self._play_context.remote_addr)
371393

372394
display.vvv(u"ibm_zos_copy: {0} {1} TO {2}".format(_sftp_action, _src, temp_path), host=self._play_context.remote_addr)
395+
if self._connection.become:
396+
was_user_updated = True
397+
self._connection.set_option('remote_user', self._play_context._become_user)
398+
display.vvv(
399+
u"ibm_zos_copy SSH transfer user updated to {0}".format(self._play_context._become_user),
400+
host=self._play_context.remote_addr
401+
)
373402
(returncode, stdout, stderr) = self._connection._file_transport_command(_src, temp_path, _sftp_action)
374403

375404
display.vvv(u"ibm_zos_copy return code: {0}".format(returncode), host=self._play_context.remote_addr)
@@ -400,7 +429,7 @@ def _copy_to_remote(self, src, is_dir=False, ignore_stderr=False):
400429

401430
if returncode != 0 or (err and not ignore_stderr):
402431
return dict(
403-
msg="Error transfering source '{0}' to remote z/OS system".format(src),
432+
msg="Error transferring source '{0}' to remote z/OS system".format(src),
404433
rc=returncode,
405434
stderr=err,
406435
stderr_lines=err.splitlines(),
@@ -409,6 +438,12 @@ def _copy_to_remote(self, src, is_dir=False, ignore_stderr=False):
409438

410439
finally:
411440
# Restore the users defined option `ssh_transfer_method` if it was overridden
441+
if was_user_updated:
442+
self._connection.set_option('remote_user', self._play_context._remote_user)
443+
display.vvv(
444+
u"ibm_zos_copy SSH transfer user restored to {0}".format(self._play_context._remote_user),
445+
host=self._play_context.remote_addr
446+
)
412447

413448
if is_ssh_transfer_method_updated:
414449
if version_major == 2 and version_minor >= 11:

plugins/action/zos_job_submit.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -77,16 +77,16 @@ def run(self, tmp=None, task_vars=None):
7777
return result
7878

7979
tmp_dir = self._connection._shell._options.get("remote_tmp")
80-
rc, stdout, stderr = self._connection.exec_command("cd {0} && pwd".format(tmp_dir))
81-
if rc > 0:
82-
msg = f"Failed to resolve remote temporary directory {tmp_dir}. Ensure that the directory exists and user has proper access."
83-
return self._exit_action({}, msg, failed=True)
84-
85-
tmp_dir = stdout.decode("utf-8").replace("\r", "").replace("\n", "")
8680
temp_file_dir = f'zos_job_submit_{datetime.now().strftime("%Y%m%d%S%f")}'
87-
dest_path = path.join(tmp_dir, temp_file_dir, path.basename(source))
88-
# Creating the name for the temp file needed.
89-
self._connection.exec_command("mkdir -p {0}".format(path.dirname(dest_path)))
81+
dest_path = path.join(tmp_dir, temp_file_dir)
82+
tempfile_args = {"path": dest_path, "state": "directory"}
83+
# Reverted this back to using file ansible module so ansible would handle all temporary dirs
84+
# creation with correct permissions.
85+
tempfile = self._execute_module(
86+
module_name="file", module_args=tempfile_args, task_vars=task_vars,
87+
)
88+
dest_path = tempfile.get("path")
89+
dest_file = path.join(dest_path, path.basename(source))
9090

9191
source_full = None
9292
try:
@@ -139,8 +139,8 @@ def run(self, tmp=None, task_vars=None):
139139
copy_module_args.update(
140140
dict(
141141
src=source_full,
142-
dest=dest_path,
143-
mode="0600",
142+
dest=dest_file,
143+
mode="0666",
144144
force=True,
145145
encoding=module_args.get('encoding'),
146146
remote_src=False,
@@ -163,7 +163,7 @@ def run(self, tmp=None, task_vars=None):
163163

164164
result.update(copy_action.run(task_vars=task_vars))
165165
if result.get("msg") is None:
166-
module_args["src"] = dest_path
166+
module_args["src"] = dest_file
167167
result.update(
168168
self._execute_module(
169169
module_name="ibm.ibm_zos_core.zos_job_submit",

tests/functional/modules/test_zos_copy_func.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,29 @@ def test_copy_local_file_to_uss_file_convert_encoding(ansible_zos_module):
648648
hosts.all.file(path=dest_path, state="absent")
649649

650650

651+
@pytest.mark.uss
652+
def test_copy_local_file_to_uss_file_with_absent_remote_tmp_dir(ansible_zos_module):
653+
hosts = ansible_zos_module
654+
dest_path = get_random_file_name(dir=TMP_DIRECTORY) + "/profile"
655+
try:
656+
hosts.all.shell(cmd="rm -rf ~/.ansible/tmp")
657+
copy_res = hosts.all.zos_copy(
658+
src="/etc/profile",
659+
dest=dest_path,
660+
encoding={"from": "ISO8859-1", "to": "IBM-1047"},
661+
)
662+
stat_res = hosts.all.stat(path=dest_path)
663+
for result in copy_res.contacted.values():
664+
assert result.get("msg") is None
665+
assert result.get("changed") is True
666+
assert result.get("dest") == dest_path
667+
assert result.get("state") == "file"
668+
for result in stat_res.contacted.values():
669+
assert result.get("stat").get("exists") is True
670+
finally:
671+
hosts.all.file(path=dest_path, state="absent")
672+
673+
651674
@pytest.mark.uss
652675
def test_copy_inline_content_to_uss_dir(ansible_zos_module):
653676
hosts = ansible_zos_module

0 commit comments

Comments
 (0)