Skip to content

Commit 40fa23f

Browse files
authored
Adds option to skip encoding for zos_unarchive module (#2136)
* testing and adding skip_encoding * changelog addition * sanity fix * review comments incorporation
1 parent 1ea3832 commit 40fa23f

File tree

3 files changed

+250
-7
lines changed

3 files changed

+250
-7
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
trivial:
2+
- zos_unarchive - Adds support for skipping encoding in zos_unarchive module.
3+
This allows users to skip encoding for certain files after unarchiving them.
4+
(https://github.com/ansible-collections/ibm_zos_core/pull/2136)

plugins/modules/zos_unarchive.py

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,12 @@
342342
- The destination I(dest) character set for the files to be written as.
343343
required: false
344344
type: str
345+
skip_encoding:
346+
description:
347+
- List of names to skip encoding after unarchiving. This is only used if I(encoding) is set, otherwise is ignored.
348+
required: false
349+
type: list
350+
elements: str
345351
attributes:
346352
action:
347353
support: full
@@ -419,6 +425,17 @@
419425
encoding:
420426
from: IBM-1047
421427
to: ISO8859-1
428+
429+
- name: Encode the destination data set into Latin-1 after unarchiving.
430+
zos_unarchive:
431+
src: "USER.ARCHIVE.RESULT.TRS"
432+
format:
433+
name: terse
434+
encoding:
435+
from: IBM-1047
436+
to: ISO8859-1
437+
skip_encoding:
438+
- USER.ARCHIVE.TEST1
422439
'''
423440

424441
RETURN = r'''
@@ -453,7 +470,11 @@
453470
List of files or data sets that were failed while encoding.
454471
type: list
455472
returned: success
456-
473+
skipped_encoding_targets:
474+
description:
475+
List of files or data sets that were skipped while encoding.
476+
type: list
477+
returned: success
457478
'''
458479

459480
import abc
@@ -525,6 +546,8 @@ def __init__(self, module):
525546
The encoding of the source file.
526547
to_encoding: str
527548
The required encoding of the destination file.
549+
skip_encoding : list[str]
550+
List of paths to exclude in encoding.
528551
"""
529552
self.module = module
530553
self.src = module.params.get("src")
@@ -547,6 +570,8 @@ def __init__(self, module):
547570
self.dest = os.path.dirname(self.src)
548571
self.encoded = list()
549572
self.failed_on_encoding = list()
573+
self.skip_encoding = encoding_param.get("skip_encoding")
574+
self.skipped_encoding_targets = list()
550575

551576
@abc.abstractmethod
552577
def extract_src(self):
@@ -591,6 +616,17 @@ def update_permissions(self):
591616
file_args = self.module.load_file_common_arguments(self.module.params, path=file_name)
592617
self.module.set_fs_attributes_if_different(file_args, self.changed)
593618

619+
def encoding_targets(self):
620+
"""Finds encoding target files in host.
621+
"""
622+
if self.skip_encoding:
623+
self.encode_targets = [
624+
path for path in self.targets if path not in self.skip_encoding
625+
]
626+
self.skipped_encoding_targets = self.skip_encoding
627+
else:
628+
self.encode_targets = self.targets
629+
594630
def encode_destination(self):
595631
"""Convert encoding for given destination
596632
Returns
@@ -602,7 +638,7 @@ def encode_destination(self):
602638
self.encoded = []
603639
self.failed_on_encoding = []
604640

605-
for target in self.targets:
641+
for target in self.encode_targets:
606642
try:
607643
file_path = os.path.normpath(os.path.join(self.dest, target))
608644
convert_rc = enc_utils.uss_convert_encoding_prev(
@@ -617,7 +653,8 @@ def encode_destination(self):
617653

618654
return {
619655
"encoded": self.encoded,
620-
"failed_on_encoding": self.failed_on_encoding
656+
"failed_on_encoding": self.failed_on_encoding,
657+
"skipped_encoding_targets": self.skipped_encoding_targets
621658
}
622659

623660
@property
@@ -636,6 +673,7 @@ def result(self):
636673
'missing': self.missing,
637674
'encoded': getattr(self, 'encoded', []),
638675
'failed_on_encoding': getattr(self, 'failed_on_encoding', []),
676+
'skipped_encoding_targets' : getattr(self, 'skipped_encoding_targets', []),
639677
}
640678

641679
def extract_all(self, members):
@@ -1201,6 +1239,17 @@ def clean_environment(self, data_sets=None, uss_files=None, remove_targets=False
12011239
for target in self.targets:
12021240
data_set.DataSet.ensure_absent(target)
12031241

1242+
def encoding_targets(self):
1243+
"""Finds encoding target datasets in host.
1244+
"""
1245+
if self.skip_encoding:
1246+
self.encode_targets = [
1247+
path for path in self.targets if path not in self.skip_encoding
1248+
]
1249+
self.skipped_encoding_targets = self.skip_encoding
1250+
else:
1251+
self.encode_targets = self.targets
1252+
12041253
def encode_destination(self):
12051254
"""Convert encoding for given destination
12061255
Returns
@@ -1212,7 +1261,7 @@ def encode_destination(self):
12121261
self.encoded = []
12131262
self.failed_on_encoding = []
12141263

1215-
for target in self.targets:
1264+
for target in self.encode_targets:
12161265
try:
12171266
ds_utils = data_set.DataSetUtils(target, tmphlq=self.tmphlq)
12181267
ds_type = ds_utils.ds_type()
@@ -1232,7 +1281,8 @@ def encode_destination(self):
12321281
self.failed_on_encoding.append(os.path.abspath(target))
12331282
return {
12341283
"encoded": self.encoded,
1235-
"failed_on_encoding": self.failed_on_encoding
1284+
"failed_on_encoding": self.failed_on_encoding,
1285+
"skipped_encoding_targets": self.skipped_encoding_targets
12361286
}
12371287

12381288

@@ -1623,6 +1673,11 @@ def run_module():
16231673
"to": dict(
16241674
type='str',
16251675
required=False,
1676+
),
1677+
"skip_encoding": dict(
1678+
type='list',
1679+
elements='str',
1680+
required=False,
16261681
)
16271682
}
16281683
),
@@ -1706,7 +1761,8 @@ def run_module():
17061761
type='dict',
17071762
options={
17081763
'from' : dict(type='str'),
1709-
"to" : dict(type='str')
1764+
'to' : dict(type='str'),
1765+
'skip_encoding' : dict(type='list', elements='str', required=False),
17101766
}
17111767
),
17121768
)
@@ -1733,10 +1789,12 @@ def run_module():
17331789

17341790
encoding = parsed_args.get("encoding")
17351791
if unarchive.dest_unarchived() and encoding:
1792+
unarchive.encoding_targets()
17361793
encoding_result = unarchive.encode_destination()
17371794
unarchive.result.update({
17381795
"encoded": encoding_result.get("encoded", []),
1739-
"failed_on_encoding": encoding_result.get("failed_on_encoding", [])
1796+
"failed_on_encoding": encoding_result.get("failed_on_encoding", []),
1797+
"skipped_encoding_targets": encoding_result.get("skipped_encoding_targets", [])
17401798
})
17411799

17421800
module.exit_json(**unarchive.result)

tests/functional/modules/test_zos_unarchive_func.py

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ def create_multiple_members(ansible_zos_module, pds_name, member_base_name, n):
142142
# - test_uss_unarchive_list
143143
# - test_uss_unarchive_copy_to_remote
144144
# - test_uss_unarchive_encoding
145+
# - test_uss_unarchive_encoding_skip_encoding
145146

146147
# Core functionality tests
147148
# Test unarchive with no options
@@ -433,6 +434,55 @@ def test_uss_unarchive_encoding(ansible_zos_module, ds_format):
433434
finally:
434435
hosts.all.file(path=f"{USS_TEMP_DIR}", state="absent")
435436

437+
438+
@pytest.mark.uss
439+
@pytest.mark.parametrize("ds_format", USS_FORMATS)
440+
def test_uss_unarchive_encoding_skip_encoding(ansible_zos_module, ds_format):
441+
try:
442+
hosts = ansible_zos_module
443+
hosts.all.file(path=f"{USS_TEMP_DIR}", state="absent")
444+
hosts.all.file(path=USS_TEMP_DIR, state="directory")
445+
set_uss_test_env(hosts, USS_TEST_FILES)
446+
dest = f"{USS_TEMP_DIR}/archive.{ds_format}"
447+
hosts.all.zos_archive(
448+
src=list(USS_TEST_FILES.keys()),
449+
dest=dest,
450+
format={
451+
"name":ds_format
452+
}
453+
)
454+
# remove files
455+
for file in USS_TEST_FILES.keys():
456+
hosts.all.file(path=file, state="absent")
457+
#specify encoding and skip_encoding file
458+
first_file_to_skip = [next(iter(USS_TEST_FILES.keys()))]
459+
encoding={
460+
"from": TO_ENCODING,
461+
"to": FROM_ENCODING,
462+
"skip_encoding": first_file_to_skip
463+
}
464+
#unarchive files
465+
unarchive_result = hosts.all.zos_unarchive(
466+
src=dest,
467+
format={
468+
"name":ds_format
469+
},
470+
remote_src=True,
471+
encoding= encoding,
472+
)
473+
hosts.all.shell(cmd=f"ls {USS_TEMP_DIR}")
474+
475+
for result in unarchive_result.contacted.values():
476+
assert result.get("failed", False) is False
477+
assert result.get("changed") is True
478+
# Command to assert the file is in place
479+
cmd_result = hosts.all.shell(cmd=f"ls {USS_TEMP_DIR}")
480+
for c_result in cmd_result.contacted.values():
481+
for file in USS_TEST_FILES.keys():
482+
assert file[len(USS_TEMP_DIR)+1:] in c_result.get("stdout")
483+
finally:
484+
hosts.all.file(path=f"{USS_TEMP_DIR}", state="absent")
485+
436486
######################################################################
437487
#
438488
# MVS data sets tests
@@ -450,6 +500,7 @@ def test_uss_unarchive_encoding(ansible_zos_module, ds_format):
450500
# - test_mvs_unarchive_remote_src
451501
# - test_mvs_unarchive_encoding
452502
# - test_mvs_unarchive_multiple_data_set_use_adrdssu_encoding
503+
# - test_mvs_unarchive_encoding_skip_encoding
453504

454505

455506
@pytest.mark.ds
@@ -1434,6 +1485,136 @@ def test_mvs_unarchive_encoding(
14341485
hosts.all.zos_data_set(name=mvs_dest_archive, state="absent")
14351486

14361487

1488+
@pytest.mark.ds
1489+
@pytest.mark.parametrize(
1490+
"ds_format", [
1491+
"terse",
1492+
"xmit"
1493+
])
1494+
@pytest.mark.parametrize(
1495+
"data_set", [
1496+
{
1497+
"dstype":"seq",
1498+
"members":[""]
1499+
},
1500+
{
1501+
"dstype":"pds",
1502+
"members":["MEM1", "MEM2"]
1503+
},
1504+
]
1505+
)
1506+
@pytest.mark.parametrize(
1507+
"record_length", [80]
1508+
)
1509+
@pytest.mark.parametrize(
1510+
"encoding", [
1511+
{"from": "IBM-1047", "to": "ISO8859-1"},
1512+
]
1513+
)
1514+
def test_mvs_unarchive_encoding_skip_encoding(
1515+
ansible_zos_module,
1516+
ds_format,
1517+
data_set,
1518+
record_length,
1519+
encoding
1520+
):
1521+
try:
1522+
hosts = ansible_zos_module
1523+
mvs_dest_archive = get_tmp_ds_name()
1524+
dataset = get_tmp_ds_name(3)
1525+
hlq = "ANSIBLE"
1526+
record_format = "fb"
1527+
# Clean env
1528+
hosts.all.zos_data_set(name=mvs_dest_archive, state="absent")
1529+
# Create source data set
1530+
hosts.all.zos_data_set(
1531+
name=dataset,
1532+
type=data_set.get("dstype"),
1533+
state="present",
1534+
record_length=record_length,
1535+
record_format=record_format,
1536+
replace=True
1537+
)
1538+
# Create members if needed
1539+
if data_set.get("dstype") in ["pds", "pdse"]:
1540+
for member in data_set.get("members"):
1541+
hosts.all.zos_data_set(
1542+
name=f"{dataset}({member})",
1543+
type="member",
1544+
state="present",
1545+
replace=True
1546+
)
1547+
test_line = "a" * record_length
1548+
for member in data_set.get("members"):
1549+
if member == "":
1550+
ds_to_write = f"{dataset}"
1551+
else:
1552+
ds_to_write = f"{dataset}({member})"
1553+
hosts.all.shell(cmd=f"decho '{test_line}' \"{ds_to_write}\"")
1554+
1555+
format_dict = {
1556+
"name":ds_format
1557+
}
1558+
if ds_format == "terse":
1559+
format_dict["format_options"] = {
1560+
"terse_pack":"spack"
1561+
}
1562+
archive_result = hosts.all.zos_archive(
1563+
src=dataset,
1564+
dest=mvs_dest_archive,
1565+
format=format_dict,
1566+
dest_data_set={
1567+
"name":dataset,
1568+
"type":"seq",
1569+
"record_format":record_format,
1570+
"record_length":record_length
1571+
},
1572+
)
1573+
# assert response is positive
1574+
for result in archive_result.contacted.values():
1575+
assert result.get("changed") is True
1576+
assert result.get("dest") == mvs_dest_archive
1577+
assert dataset in result.get("archived")
1578+
cmd_result = hosts.all.shell(cmd = f"""dls "{hlq}.*" """)
1579+
for c_result in cmd_result.contacted.values():
1580+
assert mvs_dest_archive in c_result.get("stdout")
1581+
1582+
hosts.all.zos_data_set(name=dataset, state="absent")
1583+
1584+
if ds_format == "terse":
1585+
del format_dict["format_options"]["terse_pack"]
1586+
1587+
#skipping some files to encode
1588+
skip_encoding_list = [dataset]
1589+
current_encoding_config = encoding.copy()
1590+
current_encoding_config["skip_encoding"] = skip_encoding_list
1591+
1592+
# Unarchive action
1593+
unarchive_result = hosts.all.zos_unarchive(
1594+
src=mvs_dest_archive,
1595+
format=format_dict,
1596+
remote_src=True,
1597+
dest_data_set={
1598+
"name":dataset,
1599+
"type":data_set.get("dstype"),
1600+
"record_format":record_format,
1601+
"record_length":record_length
1602+
},
1603+
encoding=encoding,
1604+
)
1605+
# assert response is positive
1606+
for result in unarchive_result.contacted.values():
1607+
assert result.get("changed") is True
1608+
assert result.get("failed", False) is False
1609+
# assert result.get("dest") == mvs_dest_archive
1610+
# assert data_set.get("name") in result.get("archived")
1611+
cmd_result = hosts.all.shell(cmd = f"""dls "{hlq}.*" """)
1612+
for c_result in cmd_result.contacted.values():
1613+
assert dataset in c_result.get("stdout")
1614+
finally:
1615+
hosts.all.zos_data_set(name=dataset, state="absent")
1616+
hosts.all.zos_data_set(name=mvs_dest_archive, state="absent")
1617+
14371618

14381619
@pytest.mark.ds
14391620
@pytest.mark.parametrize(

0 commit comments

Comments
 (0)