Skip to content

Commit 98312ba

Browse files
authored
[Bugfix] [Forward port] [zos_copy] Use of force_lock with ASA data sets (#1941)
* [Bugfix] [Forward port] [v1.13.0] [zos_copy] Use of force_lock with ASA data sets (#1939) * [Bugfix] [v1.12.1] [zos_copy] Add force_lock when copying into an ASA data set (#1932) * Add force_lock when copying into an ASA data set * Add changelog fragment * Add tests * Update changelog fragment * Fix force_lock test * Update changelog fragment
1 parent 073698c commit 98312ba

File tree

4 files changed

+106
-8
lines changed

4 files changed

+106
-8
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
bugfixes:
2+
- zos_copy - When ``asa_text`` was set to true at the same time as ``force_lock``,
3+
a copy would fail saying the destination was already in use.
4+
Fix now opens destination data sets up with disposition SHR when ``force_lock`` and
5+
``asa_text`` are set to true.
6+
(https://github.com/ansible-collections/ibm_zos_core/pull/1941).

plugins/module_utils/copy.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ def copy_vsam_ps(src, dest, tmphlq=None):
209209
return rc, out, err
210210

211211

212-
def copy_asa_uss2mvs(src, dest, tmphlq=None):
212+
def copy_asa_uss2mvs(src, dest, tmphlq=None, force_lock=False):
213213
"""Copy a file from USS to an ASA sequential data set or PDS/E member.
214214
215215
Parameters
@@ -220,6 +220,8 @@ def copy_asa_uss2mvs(src, dest, tmphlq=None):
220220
The MVS destination data set or member.
221221
tmphlq : str
222222
High Level Qualifier for temporary datasets.
223+
force_lock : bool
224+
Whether to open the destination in SHR mode.
223225
224226
Returns
225227
-------
@@ -231,12 +233,17 @@ def copy_asa_uss2mvs(src, dest, tmphlq=None):
231233
The stderr after the copy command executed successfully.
232234
"""
233235

234-
module = AnsibleModuleHelper(argument_spec={})
235-
new_dest = dest.replace('\\', '')
236-
new_source = src.replace('\\', '')
237236
# Removes escaping to execute this command
238-
oget_cmd = f"tsocmd \" OGET '{new_source}' '{new_dest}' \""
239-
rc, out, err = module.run_command(oget_cmd)
237+
dest = dest.replace('\\', '')
238+
src = src.replace('\\', '')
239+
dest_dsp = "shr" if force_lock else "old"
240+
241+
ocopy_cmd = "OCOPY INDD(DSSRC) OUTDD(DSTAR) TEXT"
242+
ocopy_dds = {
243+
"dssrc": src,
244+
"dstar": f"{dest},{dest_dsp}"
245+
}
246+
rc, out, err = ikjeft01(ocopy_cmd, dds=ocopy_dds, authorized=True, tmphlq=tmphlq)
240247

241248
return TSOCmdResponse(rc, out, err)
242249

plugins/modules/zos_copy.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,7 +1083,7 @@ def copy_to_seq(
10831083
copy_args["options"] = ""
10841084

10851085
if src_type == 'USS' and self.asa_text:
1086-
response = copy.copy_asa_uss2mvs(new_src, dest, tmphlq=self.tmphlq)
1086+
response = copy.copy_asa_uss2mvs(new_src, dest, tmphlq=self.tmphlq, force_lock=self.force_lock)
10871087

10881088
if response.rc != 0:
10891089
raise CopyOperationError(
@@ -2153,7 +2153,7 @@ def copy_to_member(
21532153
opts["options"] = ""
21542154

21552155
if src_type == 'USS' and self.asa_text:
2156-
response = copy.copy_asa_uss2mvs(src, dest, tmphlq=self.tmphlq)
2156+
response = copy.copy_asa_uss2mvs(src, dest, tmphlq=self.tmphlq, force_lock=self.force_lock)
21572157
rc, out, err = response.rc, response.stdout_response, response.stderr_response
21582158
else:
21592159
# While ASA files are just text files, we do a binary copy

tests/functional/modules/test_zos_copy_func.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2088,6 +2088,91 @@ def test_copy_dest_lock(ansible_zos_module, ds_type, f_lock ):
20882088
hosts.all.zos_data_set(name=data_set_2, state="absent")
20892089

20902090

2091+
@pytest.mark.seq
2092+
@pytest.mark.pdse
2093+
@pytest.mark.asa
2094+
@pytest.mark.parametrize("ds_type, f_lock",[
2095+
( "pds", True), # Success path, pds locked, force_lock enabled and user authorized
2096+
( "pdse", True), # Success path, pdse locked, force_lock enabled and user authorized
2097+
( "seq", True), # Success path, seq locked, force_lock enabled and user authorized
2098+
( "pds", False), # Module exits with: Unable to write to dest '{0}' because a task is accessing the data set."
2099+
( "pdse", False), # Module exits with: Unable to write to dest '{0}' because a task is accessing the data set."
2100+
( "seq", False), # Module exits with: Unable to write to dest '{0}' because a task is accessing the data set."
2101+
])
2102+
def test_copy_asa_dest_lock(ansible_zos_module, ds_type, f_lock ):
2103+
hosts = ansible_zos_module
2104+
data_set = get_tmp_ds_name(llq_size=4)
2105+
data_set = f"{data_set}$#@"
2106+
member_name = "MEM1"
2107+
2108+
if ds_type == "pds" or ds_type == "pdse":
2109+
dest_data_set = f"{data_set}({member_name})"
2110+
else:
2111+
dest_data_set = data_set
2112+
2113+
try:
2114+
hosts.all.zos_data_set(name=data_set, state="present", type=ds_type, replace=True, record_format="fba")
2115+
if ds_type == "pds" or ds_type == "pdse":
2116+
hosts.all.zos_data_set(name=dest_data_set, state="present", type="member", replace=True)
2117+
2118+
# copy/compile c program and copy jcl to hold data set lock for n seconds in background(&)
2119+
temp_dir = get_random_file_name(dir=TMP_DIRECTORY)
2120+
hosts.all.zos_copy(content=c_pgm, dest=f'{temp_dir}/pdse-lock.c', force=True)
2121+
hosts.all.zos_copy(
2122+
content=call_c_jcl.format(temp_dir, dest_data_set),
2123+
dest=f'{temp_dir}/call_c_pgm.jcl',
2124+
force=True
2125+
)
2126+
hosts.all.shell(cmd="xlc -o pdse-lock pdse-lock.c", chdir=f"{temp_dir}/")
2127+
# submit jcl
2128+
hosts.all.shell(cmd="submit call_c_pgm.jcl", chdir=f"{temp_dir}/")
2129+
# pause to ensure c code acquires lock
2130+
time.sleep(5)
2131+
2132+
results = hosts.all.zos_copy(
2133+
content=ASA_SAMPLE_CONTENT,
2134+
dest=dest_data_set,
2135+
remote_src=False,
2136+
asa_text=True,
2137+
force=True,
2138+
force_lock=f_lock
2139+
)
2140+
2141+
for result in results.contacted.values():
2142+
print(result)
2143+
if f_lock: #and apf_auth_user:
2144+
assert result.get("changed") is True
2145+
assert result.get("msg") is None
2146+
2147+
# We need to escape the data set name because we are using cat, using dcat will
2148+
# bring the trailing empty spaces according to the data set record length.
2149+
# We only need to escape $ character in this notation
2150+
dest_escaped = dest_data_set.replace('$', '\\$')
2151+
verify_copy = hosts.all.shell(
2152+
cmd="cat \"//'{0}'\"".format(dest_escaped),
2153+
executable=SHELL_EXECUTABLE,
2154+
)
2155+
2156+
for v_cp in verify_copy.contacted.values():
2157+
assert v_cp.get("rc") == 0
2158+
assert v_cp.get("stdout") == ASA_SAMPLE_RETURN
2159+
else:
2160+
assert result.get("failed") is True
2161+
assert result.get("changed") is False
2162+
assert "because a task is accessing the data set" in result.get("msg")
2163+
assert result.get("rc") is None
2164+
finally:
2165+
# extract pid
2166+
ps_list_res = hosts.all.shell(cmd="ps -e | grep -i 'pdse-lock'")
2167+
# kill process - release lock - this also seems to end the job
2168+
pid = list(ps_list_res.contacted.values())[0].get('stdout').strip().split(' ')[0]
2169+
hosts.all.shell(cmd="kill 9 {0}".format(pid.strip()))
2170+
# clean up c code/object/executable files, jcl
2171+
hosts.all.shell(cmd=f'rm -r {temp_dir}')
2172+
# remove destination data set.
2173+
hosts.all.zos_data_set(name=data_set, state="absent")
2174+
2175+
20912176
@pytest.mark.uss
20922177
@pytest.mark.seq
20932178
def test_copy_file_record_length_to_sequential_data_set(ansible_zos_module):

0 commit comments

Comments
 (0)