Skip to content

Commit 7796efc

Browse files
[Enhancement] [zos_copy] supporting alias names in src and dest for PS, PDS and PDSE data sets (#2103)
* Update zos_copy.py * Adding testcases * Update zos_copy.py * Updating logic and testcases * Update data_set.py * Create 2103-zos_copy-supporting-aliases-for-src-and-dest.yml * Update 2103-zos_copy-supporting-aliases-for-src-and-dest.yml * Update data_set.py * Update changelogs/fragments/2103-zos_copy-supporting-aliases-for-src-and-dest.yml Co-authored-by: Fernando Flores <[email protected]> * Update test_zos_copy_func.py --------- Co-authored-by: Fernando Flores <[email protected]>
1 parent 8e1de0d commit 7796efc

File tree

4 files changed

+256
-13
lines changed

4 files changed

+256
-13
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
minor_changes:
2+
- zos_copy - Adds support of using alias names in src and dest parameters for PS, PDS and PDSE data sets.
3+
(https://github.com/ansible-collections/ibm_zos_core/pull/2103)

plugins/module_utils/data_set.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2181,24 +2181,23 @@ def get_name_if_data_set_is_alias(name, tmp_hlq=None):
21812181
# We need to unescape because this call to the system can handle
21822182
# special characters just fine.
21832183
name = name.upper().replace("\\", '')
2184-
idcams_cmd = f" LISTCAT ENTRIES('{name}') ALL"
2184+
idcams_cmd = f" LISTCAT ALIAS ENTRIES('{name}')ALL"
21852185
response = DataSet._execute_idcams_cmd(idcams_cmd, tmp_hlq=tmp_hlq)
21862186

2187-
if response.rc > 0 or response.stderr_response != '':
2188-
raise MVSCmdExecError(
2189-
rc=response.rc,
2190-
stdout=response.stdout_response,
2191-
stderr=response.stderr_response
2192-
)
2193-
2194-
if re.search(r'(ALIAS -+)(1)', response.stdout_response):
2187+
if response.rc == 0:
21952188
base_name = re.search(
21962189
r'(ASSOCIATIONS\s*\n\s*[0-9a-zA-Z]+-+)([0-9a-zA-Z\.@\$#-]+)',
21972190
response.stdout_response
21982191
).group(2)
21992192
return True, base_name
2200-
else:
2193+
elif response.rc == 4:
22012194
return False, name
2195+
elif response.rc != 0 or response.stderr_response != '':
2196+
raise MVSCmdExecError(
2197+
rc=response.rc,
2198+
out=response.stdout_response,
2199+
err=response.stderr_response
2200+
)
22022201

22032202
@staticmethod
22042203
def _execute_idcams_cmd(

plugins/modules/zos_copy.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
description:
9595
- The remote absolute path or data set where the content should be copied to.
9696
- C(dest) can be a USS file, directory or MVS data set name.
97+
- C(dest) can be a alias name of a PS, PDS or PDSE data set.
9798
- If C(dest) has missing parent directories, they will be created.
9899
- If C(dest) is a nonexistent USS file, it will be created.
99100
- If C(dest) is a new USS file or replacement, the file will be appropriately tagged with
@@ -304,6 +305,7 @@
304305
description:
305306
- Path to a file/directory or name of a data set to copy to remote
306307
z/OS system.
308+
- C(src) can be a alias name of a PS, PDS or PDSE data set.
307309
- If C(remote_src) is true, then C(src) must be the path to a Unix
308310
System Services (USS) file, name of a data set, or data set member.
309311
- If C(src) is a local path or a USS path, it can be absolute or relative.
@@ -3379,6 +3381,17 @@ def run_module(module, arg_def):
33793381
src_member = is_member(src)
33803382
raw_src = src
33813383
raw_dest = dest
3384+
is_src_alias = False
3385+
is_dest_alias = False
3386+
3387+
if is_mvs_src and not src_member and not is_src_gds:
3388+
is_src_alias, src_base_name = data_set.DataSet.get_name_if_data_set_is_alias(src, tmphlq)
3389+
if is_src_alias:
3390+
src = src_base_name
3391+
if is_mvs_dest and not copy_member and not is_dest_gds:
3392+
is_dest_alias, dest_base_name = data_set.DataSet.get_name_if_data_set_is_alias(dest, tmphlq)
3393+
if is_dest_alias:
3394+
dest = dest_base_name
33823395

33833396
# Validation for copy from a member
33843397
if src_member:
@@ -3910,8 +3923,8 @@ def run_module(module, arg_def):
39103923

39113924
res_args.update(
39123925
dict(
3913-
src=src,
3914-
dest=dest,
3926+
src=module.params.get('src') if is_src_alias else src,
3927+
dest=module.params.get('dest') if is_dest_alias else dest,
39153928
ds_type=dest_ds_type,
39163929
dest_exists=dest_exists,
39173930
backup_name=backup_name,

tests/functional/modules/test_zos_copy_func.py

Lines changed: 229 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import tempfile
2424
import subprocess
2525
from shellescape import quote
26-
2726
from ibm_zos_core.tests.helpers.volumes import Volume_Handler
2827
from ibm_zos_core.tests.helpers.dataset import get_tmp_ds_name
2928
from ibm_zos_core.tests.helpers.utils import get_random_file_name
@@ -5811,3 +5810,232 @@ def test_job_script_async(ansible_zos_module, get_config):
58115810
assert result.stderr == ""
58125811
finally:
58135812
ansible_zos_module.all.zos_data_set(name=ds_name, state="absent")
5813+
5814+
def test_copy_data_set_seq_with_aliases(ansible_zos_module, volumes_on_systems):
5815+
hosts = ansible_zos_module
5816+
src = get_tmp_ds_name(mlq_size=3, llq_size=3)
5817+
src_alias = get_tmp_ds_name(mlq_size=3, llq_size=3)
5818+
src = f'{src[:7]}.{src[13:]}'
5819+
src_alias = f'{src_alias[:7]}.{src_alias[13:]}'
5820+
dest = get_tmp_ds_name(mlq_size=3, llq_size=3)
5821+
dest_alias = get_tmp_ds_name(mlq_size=3, llq_size=3)
5822+
dest = f'{dest[:7]}.{dest[13:]}'
5823+
dest_alias = f'{dest_alias[:7]}.{dest_alias[13:]}'
5824+
volumes = Volume_Handler(volumes_on_systems)
5825+
available_vol = volumes.get_available_vol()
5826+
try:
5827+
data_set_creation_result = hosts.all.shell(
5828+
cmd=f'dtouch -tseq -V{available_vol} {src}'
5829+
)
5830+
hosts.all.shell(cmd=f"""decho "{DUMMY_DATA}" "{src}" """)
5831+
for result in data_set_creation_result.contacted.values():
5832+
assert result.get('changed') is True
5833+
assert result.get('failed', False) is False
5834+
dest_data_set_creation_result = hosts.all.shell(
5835+
cmd=f'dtouch -tseq {dest}'
5836+
)
5837+
for result in dest_data_set_creation_result.contacted.values():
5838+
assert result.get('changed') is True
5839+
assert result.get('failed', False) is False
5840+
alias_creation_result = hosts.all.shell(
5841+
cmd=f'echo " DEFINE ALIAS (NAME({src_alias}) RELATE({src}))" | mvscmdauth --pgm=idcams --sysin=stdin --sysprint=*'
5842+
)
5843+
for result in alias_creation_result.contacted.values():
5844+
assert result.get('changed') is True
5845+
assert result.get('failed', False) is False
5846+
dest_alias_creation_result = hosts.all.shell(
5847+
cmd=f'echo " DEFINE ALIAS (NAME({dest_alias}) RELATE({dest}))" | mvscmdauth --pgm=idcams --sysin=stdin --sysprint=*'
5848+
)
5849+
for result in dest_alias_creation_result.contacted.values():
5850+
assert result.get('changed') is True
5851+
assert result.get('failed', False) is False
5852+
zos_copy_result = hosts.all.zos_copy(
5853+
src=src_alias,
5854+
dest=dest_alias,
5855+
remote_src=True
5856+
)
5857+
for result in zos_copy_result.contacted.values():
5858+
assert result.get('changed') is True
5859+
assert result.get('failed', False) is False
5860+
verify_copy = hosts.all.shell(
5861+
cmd="cat \"//'{0}'\"".format(dest),
5862+
executable=SHELL_EXECUTABLE,
5863+
)
5864+
for v_cp in verify_copy.contacted.values():
5865+
assert v_cp.get("rc") == 0
5866+
assert v_cp.get("stdout") == DUMMY_DATA
5867+
finally:
5868+
hosts.all.shell(cmd=f'drm {src_alias}')
5869+
hosts.all.shell(cmd=f'drm {dest_alias}')
5870+
hosts.all.shell(cmd=f'drm {src}')
5871+
hosts.all.shell(cmd=f'drm {dest}')
5872+
5873+
def test_copy_pds_to_pds_using_dest_alias(ansible_zos_module):
5874+
hosts = ansible_zos_module
5875+
5876+
try:
5877+
src_pds = get_tmp_ds_name(mlq_size=3, llq_size=3)
5878+
src_pds = f'{src_pds[:7]}.{src_pds[13:]}'
5879+
dest_pds = get_tmp_ds_name(mlq_size=3, llq_size=3)
5880+
dest_pds = f'{dest_pds[:7]}.{dest_pds[13:]}'
5881+
dest_pds_alias = get_tmp_ds_name(mlq_size=3, llq_size=3)
5882+
dest_pds_alias = f'{dest_pds_alias[:7]}.{dest_pds_alias[13:]}'
5883+
5884+
hosts.all.shell(cmd=f"dtouch -tPDS {src_pds}")
5885+
hosts.all.shell(cmd=f"""decho "{DUMMY_DATA}" "{src_pds}(MEMBER)" """)
5886+
hosts.all.shell(cmd=f"dtouch -tPDS {dest_pds}")
5887+
hosts.all.shell(
5888+
cmd=f'echo " DEFINE ALIAS (NAME({dest_pds_alias}) RELATE({dest_pds}))" | mvscmdauth --pgm=idcams --sysin=stdin --sysprint=*'
5889+
)
5890+
5891+
copy_results = hosts.all.zos_copy(
5892+
src=src_pds,
5893+
dest=dest_pds_alias,
5894+
remote_src=True
5895+
)
5896+
5897+
for cp_res in copy_results.contacted.values():
5898+
assert cp_res.get("msg") is None
5899+
assert cp_res.get("changed") is True
5900+
5901+
verify_dest = hosts.all.shell(
5902+
cmd=f"""dcat "{dest_pds}(MEMBER)" """,
5903+
executable=SHELL_EXECUTABLE,
5904+
)
5905+
for v_res in verify_dest.contacted.values():
5906+
assert v_res.get("rc") == 0
5907+
assert len(v_res.get("stdout_lines")) > 0
5908+
finally:
5909+
hosts.all.shell(cmd=f"drm {src_pds}")
5910+
hosts.all.shell(cmd=f"drm {dest_pds}")
5911+
hosts.all.shell(cmd=f"drm {dest_pds_alias}")
5912+
5913+
5914+
@pytest.mark.pdse
5915+
@pytest.mark.loadlib
5916+
@pytest.mark.aliases
5917+
def test_copy_pdse_loadlib_to_pdse_loadlib_using_aliases(ansible_zos_module):
5918+
5919+
hosts = ansible_zos_module
5920+
mlq_size = 3
5921+
cobol_src_pds = get_tmp_ds_name(mlq_size)
5922+
cobol_src_mem = "HELLOCBL"
5923+
cobol_src_mem2 = "HICBL2"
5924+
src_lib = get_tmp_ds_name(mlq_size=3, llq_size=3)
5925+
src_lib = f'{src_lib[:7]}.{src_lib[13:]}'
5926+
src_lib_aliases = get_tmp_ds_name(mlq_size=3, llq_size=3)
5927+
src_lib_aliases = f'{src_lib_aliases[:7]}.{src_lib_aliases[13:]}'
5928+
dest_lib = get_tmp_ds_name(mlq_size=3, llq_size=3)
5929+
dest_lib = f'{dest_lib[:7]}.{dest_lib[13:]}'
5930+
dest_lib_aliases = get_tmp_ds_name(mlq_size=3, llq_size=3)
5931+
dest_lib_aliases = f'{dest_lib_aliases[:7]}.{dest_lib_aliases[13:]}'
5932+
pgm_mem = "HELLO"
5933+
pgm2_mem = "HELLO2"
5934+
pgm_mem_alias = "ALIAS1"
5935+
pgm2_mem_alias = "ALIAS2"
5936+
try:
5937+
# allocate pds for cobol src code
5938+
hosts.all.zos_data_set(
5939+
name=cobol_src_pds,
5940+
state="present",
5941+
type="pds",
5942+
space_primary=2,
5943+
record_format="fb",
5944+
record_length=80,
5945+
block_size=3120,
5946+
replace=True,
5947+
)
5948+
# allocate pds for src loadlib
5949+
hosts.all.zos_data_set(
5950+
name=src_lib,
5951+
state="present",
5952+
type="pdse",
5953+
record_format="u",
5954+
record_length=0,
5955+
block_size=32760,
5956+
space_primary=2,
5957+
space_type="m",
5958+
replace=True
5959+
)
5960+
5961+
hosts.all.zos_data_set(
5962+
name=dest_lib,
5963+
state="present",
5964+
type="pdse",
5965+
record_format="u",
5966+
record_length=0,
5967+
block_size=32760,
5968+
space_primary=2,
5969+
space_type="m",
5970+
replace=True
5971+
)
5972+
5973+
# generate loadlib w 2 members w 1 alias each
5974+
generate_loadlib(
5975+
hosts=hosts,
5976+
cobol_src_pds=cobol_src_pds,
5977+
cobol_src_mems=[cobol_src_mem, cobol_src_mem2],
5978+
loadlib_pds=src_lib,
5979+
loadlib_mems=[pgm_mem, pgm2_mem],
5980+
loadlib_alias_mems=[pgm_mem_alias, pgm2_mem_alias]
5981+
)
5982+
5983+
hosts.all.shell(
5984+
cmd=f'echo " DEFINE ALIAS (NAME({src_lib_aliases}) RELATE({src_lib}))" | mvscmdauth --pgm=idcams --sysin=stdin --sysprint=*'
5985+
)
5986+
5987+
hosts.all.shell(
5988+
cmd=f'echo " DEFINE ALIAS (NAME({dest_lib_aliases}) RELATE({dest_lib}))" | mvscmdauth --pgm=idcams --sysin=stdin --sysprint=*'
5989+
)
5990+
5991+
# copy src loadlib to dest library pds w aliases
5992+
copy_res_aliases = hosts.all.zos_copy(
5993+
src="{0}".format(src_lib_aliases),
5994+
dest="{0}".format(dest_lib_aliases),
5995+
remote_src=True,
5996+
executable=True,
5997+
aliases=True,
5998+
dest_data_set={
5999+
'type': "library",
6000+
'record_format': "u",
6001+
'record_length': 0,
6002+
'block_size': 32760,
6003+
'space_primary': 2,
6004+
'space_type': "m",
6005+
}
6006+
)
6007+
6008+
for result in copy_res_aliases.contacted.values():
6009+
assert result.get("msg") is None
6010+
assert result.get("changed") is True
6011+
assert result.get("dest") == "{0}".format(dest_lib_aliases)
6012+
6013+
verify_copy_mls_aliases = hosts.all.shell(
6014+
cmd="mls {0}".format(dest_lib),
6015+
executable=SHELL_EXECUTABLE
6016+
)
6017+
6018+
for v_cp in verify_copy_mls_aliases.contacted.values():
6019+
assert v_cp.get("rc") == 0
6020+
stdout = v_cp.get("stdout")
6021+
assert stdout is not None
6022+
expected_mls_str = "{0} ALIAS({1})".format(pgm_mem, pgm_mem_alias)
6023+
expected_mls_str2 = "{0} ALIAS({1})".format(pgm2_mem, pgm2_mem_alias)
6024+
assert expected_mls_str in stdout
6025+
assert expected_mls_str2 in stdout
6026+
6027+
# verify pgms remain executable
6028+
pgm_output_map = {
6029+
(dest_lib, pgm_mem, COBOL_PRINT_STR),
6030+
(dest_lib, pgm2_mem, COBOL_PRINT_STR2)
6031+
}
6032+
for steplib, pgm, output in pgm_output_map:
6033+
validate_loadlib_pgm(hosts, steplib=steplib, pgm_name=pgm, expected_output_str=output)
6034+
6035+
finally:
6036+
hosts.all.zos_data_set(name=cobol_src_pds, state="absent")
6037+
hosts.all.zos_data_set(name=src_lib, state="absent")
6038+
hosts.all.zos_data_set(name=dest_lib, state="absent")
6039+
hosts.all.zos_data_set(name=src_lib_aliases, state="absent")
6040+
hosts.all.zos_data_set(name=dest_lib_aliases, state="absent")
6041+

0 commit comments

Comments
 (0)