Skip to content

Commit 498e325

Browse files
Merge branch 'dev' into enabler/2097/test_privilege_escalation
2 parents 8954828 + 37d80c7 commit 498e325

File tree

3 files changed

+453
-4
lines changed

3 files changed

+453
-4
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
minor_changes:
2+
- zos_unarchive - Added encoding support for the unarchive module.
3+
This allows users to encode the files after unarchiving them in a perticular encoding.
4+
(https://github.com/ansible-collections/ibm_zos_core/pull/2105)

plugins/modules/zos_unarchive.py

Lines changed: 178 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
suboptions:
4949
name:
5050
description:
51-
- The compression format to use.
51+
- The compression format used while archiving.
5252
type: str
5353
required: true
5454
choices:
@@ -78,7 +78,7 @@
7878
type: str
7979
use_adrdssu:
8080
description:
81-
- If set to true, the C(zos_archive) module will use Data
81+
- If set to true, the C(zos_unarchive) module will use Data
8282
Facility Storage Management Subsystem data set services
8383
(DFSMSdss) program ADRDSSU to uncompress data sets from
8484
a portable format after using C(xmit) or C(terse).
@@ -312,7 +312,36 @@
312312
type: bool
313313
required: false
314314
default: false
315-
315+
encoding:
316+
description:
317+
- Specifies the character encoding conversion to be applied to the
318+
destination files after unarchiving.
319+
- Supported character sets rely on the charset conversion utility
320+
C(iconv) version the most common character sets are supported.
321+
- After conversion the files are stored in same location as they
322+
were unarchived to under the same original name. No backup of the
323+
original unconverted files is there as for that unarchive can be
324+
executed again without encoding params on same source archive files.
325+
- Destination files will be converted to the new encoding and will not
326+
be restored to their original encoding.
327+
- If encoding fails for any file in a set of multiple files, an
328+
exception will be raised and the name of the file skipped will be
329+
provided completing the task successfully with rc code 0.
330+
- Encoding does not check if the file is already present or not.
331+
It works on the file/files successfully unarchived.
332+
type: dict
333+
required: false
334+
suboptions:
335+
from:
336+
description:
337+
- The character set of the source I(src).
338+
required: false
339+
type: str
340+
to:
341+
description:
342+
- The destination I(dest) character set for the files to be written as.
343+
required: false
344+
type: str
316345
attributes:
317346
action:
318347
support: full
@@ -380,6 +409,16 @@
380409
format_options:
381410
use_adrdssu: true
382411
list: true
412+
413+
# Encoding example
414+
- name: Encode the destination data set into Latin-1 after unarchiving.
415+
zos_unarchive:
416+
src: "USER.ARCHIVE.RESULT.TRS"
417+
format:
418+
name: terse
419+
encoding:
420+
from: IBM-1047
421+
to: ISO8859-1
383422
'''
384423

385424
RETURN = r'''
@@ -404,6 +443,17 @@
404443
Any files or data sets not found during extraction.
405444
type: str
406445
returned: success
446+
encoded:
447+
description:
448+
List of files or data sets that were successfully encoded.
449+
type: list
450+
returned: success
451+
failed_on_encoding:
452+
description:
453+
List of files or data sets that were failed while encoding.
454+
type: list
455+
returned: success
456+
407457
'''
408458

409459
import abc
@@ -412,7 +462,8 @@
412462
better_arg_parser,
413463
data_set,
414464
validation,
415-
mvs_cmd)
465+
mvs_cmd,
466+
encode)
416467
import re
417468
import os
418469
import zipfile
@@ -470,6 +521,10 @@ def __init__(self, module):
470521
Paths that were on include, but are missing from the files in archive.
471522
remote_src : bool
472523
If the source is remote or not.
524+
from_encoding: str
525+
The encoding of the source file.
526+
to_encoding: str
527+
The required encoding of the destination file.
473528
"""
474529
self.module = module
475530
self.src = module.params.get("src")
@@ -485,8 +540,13 @@ def __init__(self, module):
485540
self.changed = False
486541
self.missing = list()
487542
self.remote_src = module.params.get("remote_src")
543+
encoding_param = module.params.get("encoding") or {}
544+
self.from_encoding = encoding_param.get("from")
545+
self.to_encoding = encoding_param.get("to")
488546
if self.dest == '':
489547
self.dest = os.path.dirname(self.src)
548+
self.encoded = list()
549+
self.failed_on_encoding = list()
490550

491551
@abc.abstractmethod
492552
def extract_src(self):
@@ -531,6 +591,35 @@ def update_permissions(self):
531591
file_args = self.module.load_file_common_arguments(self.module.params, path=file_name)
532592
self.module.set_fs_attributes_if_different(file_args, self.changed)
533593

594+
def encode_destination(self):
595+
"""Convert encoding for given destination
596+
Returns
597+
-------
598+
Union
599+
encoded or failed_on_encoding list
600+
"""
601+
enc_utils = encode.EncodeUtils()
602+
self.encoded = []
603+
self.failed_on_encoding = []
604+
605+
for target in self.targets:
606+
try:
607+
file_path = os.path.normpath(os.path.join(self.dest, target))
608+
convert_rc = enc_utils.uss_convert_encoding_prev(
609+
file_path, file_path, self.from_encoding, self.to_encoding
610+
)
611+
if convert_rc:
612+
enc_utils.uss_tag_encoding(file_path, self.to_encoding)
613+
self.encoded.append(os.path.abspath(target))
614+
615+
except Exception:
616+
self.failed_on_encoding.append(os.path.abspath(target))
617+
618+
return {
619+
"encoded": self.encoded,
620+
"failed_on_encoding": self.failed_on_encoding
621+
}
622+
534623
@property
535624
def result(self):
536625
"""Result.
@@ -545,6 +634,8 @@ def result(self):
545634
'changed': self.changed,
546635
'targets': self.targets,
547636
'missing': self.missing,
637+
'encoded': getattr(self, 'encoded', []),
638+
'failed_on_encoding': getattr(self, 'failed_on_encoding', []),
548639
}
549640

550641
def extract_all(self, members):
@@ -1050,6 +1141,8 @@ def extract_src(self):
10501141
if not self.use_adrdssu:
10511142
temp_ds, rc = self._create_dest_data_set(**self.dest_data_set)
10521143
rc = self.unpack(self.src, temp_ds)
1144+
self.targets = [temp_ds]
1145+
10531146
else:
10541147
temp_ds, rc = self._create_dest_data_set(type="seq",
10551148
record_format="u",
@@ -1108,6 +1201,40 @@ def clean_environment(self, data_sets=None, uss_files=None, remove_targets=False
11081201
for target in self.targets:
11091202
data_set.DataSet.ensure_absent(target)
11101203

1204+
def encode_destination(self):
1205+
"""Convert encoding for given destination
1206+
Returns
1207+
-------
1208+
Union
1209+
encoded or failed_on_encoding list
1210+
"""
1211+
enc_utils = encode.EncodeUtils()
1212+
self.encoded = []
1213+
self.failed_on_encoding = []
1214+
1215+
for target in self.targets:
1216+
try:
1217+
ds_utils = data_set.DataSetUtils(target, tmphlq=self.tmphlq)
1218+
ds_type = ds_utils.ds_type()
1219+
if not ds_type:
1220+
ds_type = "PS"
1221+
enc_utils.mvs_convert_encoding(
1222+
target,
1223+
target,
1224+
self.from_encoding,
1225+
self.to_encoding,
1226+
src_type=ds_type,
1227+
dest_type=ds_type,
1228+
tmphlq=self.tmphlq
1229+
)
1230+
self.encoded.append(os.path.abspath(target))
1231+
except Exception:
1232+
self.failed_on_encoding.append(os.path.abspath(target))
1233+
return {
1234+
"encoded": self.encoded,
1235+
"failed_on_encoding": self.failed_on_encoding
1236+
}
1237+
11111238

11121239
class AMATerseUnarchive(MVSUnarchive):
11131240
def __init__(self, module):
@@ -1379,6 +1506,24 @@ def __init__(self, tarinfo, path):
13791506
super().__init__()
13801507

13811508

1509+
class EncodeError(Exception):
1510+
def __init__(self, message):
1511+
"""Error during encoding.
1512+
1513+
Parameters
1514+
----------
1515+
message : str
1516+
Human readable string describing the exception.
1517+
1518+
Attributes
1519+
----------
1520+
msg : str
1521+
Human readable string describing the exception.
1522+
"""
1523+
self.msg = 'An error occurred during encoding: "{0}"'.format(message)
1524+
super(EncodeError, self).__init__(self.msg)
1525+
1526+
13821527
def run_module():
13831528
"""Initialize module.
13841529
Raises
@@ -1467,6 +1612,20 @@ def run_module():
14671612
tmp_hlq=dict(type='str'),
14681613
force=dict(type='bool', default=False),
14691614
remote_src=dict(type='bool', default=False),
1615+
encoding=dict(
1616+
type='dict',
1617+
required=False,
1618+
options={
1619+
'from': dict(
1620+
type='str',
1621+
required=False,
1622+
),
1623+
"to": dict(
1624+
type='str',
1625+
required=False,
1626+
)
1627+
}
1628+
),
14701629
),
14711630
mutually_exclusive=[
14721631
['include', 'exclude'],
@@ -1543,6 +1702,13 @@ def run_module():
15431702
mutually_exclusive=[
15441703
['include', 'exclude'],
15451704
],
1705+
encoding=dict(
1706+
type='dict',
1707+
options={
1708+
'from' : dict(type='str'),
1709+
"to" : dict(type='str')
1710+
}
1711+
),
15461712
)
15471713

15481714
try:
@@ -1565,6 +1731,14 @@ def run_module():
15651731
if unarchive.dest_unarchived() and unarchive.dest_type() == "USS":
15661732
unarchive.update_permissions()
15671733

1734+
encoding = parsed_args.get("encoding")
1735+
if unarchive.dest_unarchived() and encoding:
1736+
encoding_result = unarchive.encode_destination()
1737+
unarchive.result.update({
1738+
"encoded": encoding_result.get("encoded", []),
1739+
"failed_on_encoding": encoding_result.get("failed_on_encoding", [])
1740+
})
1741+
15681742
module.exit_json(**unarchive.result)
15691743

15701744

0 commit comments

Comments
 (0)