Skip to content

Commit c9bc328

Browse files
Merge branch 'dev' into enabler/update-test-suits-pylint/zos-copy-func
2 parents 544f75c + 148a35e commit c9bc328

21 files changed

+2288
-1285
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_fetch - add support for fetching generation data groups and
3+
generation data sets.
4+
(https://github.com/ansible-collections/ibm_zos_core/pull/1519)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
minor_changes:
2+
- zos_mvs_raw - Added support for GDG and GDS relative name notation to use a data set.
3+
Added support for data set names with special characters like $, /#, /- and @.
4+
(https://github.com/ansible-collections/ibm_zos_core/pull/1525).
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
minor_changes:
2+
- zos_data_set - Added support for GDS relative name notation to include or exclude data sets when
3+
operation is backup. Added support for data set names with special characters
4+
like $, /#, and @.
5+
(https://github.com/ansible-collections/ibm_zos_core/pull/1527).

docs/source/modules/zos_fetch.rst

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@ zos_fetch -- Fetch data from z/OS
1616

1717
Synopsis
1818
--------
19-
- This module fetches a UNIX System Services (USS) file, PS (sequential data set), PDS, PDSE, member of a PDS or PDSE, or KSDS (VSAM data set) from a remote z/OS system.
19+
- This module fetches a UNIX System Services (USS) file, PS (sequential data set), PDS, PDSE, member of a PDS or PDSE, generation data set (GDS), generation data group (GDG), or KSDS (VSAM data set) from a remote z/OS system.
2020
- When fetching a sequential data set, the destination file name will be the same as the data set name.
2121
- When fetching a PDS or PDSE, the destination will be a directory with the same name as the PDS or PDSE.
2222
- When fetching a PDS/PDSE member, destination will be a file.
2323
- Files that already exist at \ :literal:`dest`\ will be overwritten if they are different than \ :literal:`src`\ .
24+
- When fetching a GDS, the relative name will be resolved to its absolute one.
25+
- When fetching a generation data group, the destination will be a directory with the same name as the GDG.
2426

2527

2628

@@ -31,7 +33,7 @@ Parameters
3133

3234

3335
src
34-
Name of a UNIX System Services (USS) file, PS (sequential data set), PDS, PDSE, member of a PDS, PDSE or KSDS (VSAM data set).
36+
Name of a UNIX System Services (USS) file, PS (sequential data set), PDS, PDSE, member of a PDS, PDSE, GDS, GDG or KSDS (VSAM data set).
3537

3638
USS file paths should be absolute paths.
3739

@@ -187,6 +189,24 @@ Examples
187189
to: ISO8859-1
188190
flat: true
189191

192+
- name: Fetch the current generation data set from a GDG
193+
zos_fetch:
194+
src: USERHLQ.DATA.SET(0)
195+
dest: /tmp/
196+
flat: true
197+
198+
- name: Fetch a previous generation data set from a GDG
199+
zos_fetch:
200+
src: USERHLQ.DATA.SET(-3)
201+
dest: /tmp/
202+
flat: true
203+
204+
- name: Fetch a generation data group
205+
zos_fetch:
206+
src: USERHLQ.TEST.GDG
207+
dest: /tmp/
208+
flat: true
209+
190210

191211

192212

plugins/action/zos_fetch.py

Lines changed: 75 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,15 @@
2626
from ansible.utils.display import Display
2727
from ansible import cli
2828

29-
from ansible_collections.ibm.ibm_zos_core.plugins.module_utils import encode, validation
29+
from ansible_collections.ibm.ibm_zos_core.plugins.module_utils import encode, validation, data_set
3030

31-
SUPPORTED_DS_TYPES = frozenset({"PS", "PO", "VSAM", "USS"})
31+
SUPPORTED_DS_TYPES = frozenset({
32+
"PS", "SEQ", "BASIC",
33+
"PO", "PE", "PDS", "PDSE",
34+
"VSAM", "KSDS",
35+
"GDG",
36+
"USS"
37+
})
3238

3339
display = Display()
3440

@@ -37,10 +43,15 @@ def _update_result(result, src, dest, ds_type="USS", is_binary=False):
3743
""" Helper function to update output result with the provided values """
3844
data_set_types = {
3945
"PS": "Sequential",
46+
"SEQ": "Sequential",
47+
"BASIC": "Sequential",
4048
"PO": "Partitioned",
41-
"PDSE": "Partitioned Extended",
49+
"PDS": "Partitioned",
4250
"PE": "Partitioned Extended",
51+
"PDSE": "Partitioned Extended",
4352
"VSAM": "VSAM",
53+
"KSDS": "VSAM",
54+
"GDG": "Generation Data Group",
4455
"USS": "USS",
4556
}
4657
updated_result = dict((k, v) for k, v in result.items())
@@ -101,6 +112,7 @@ class ActionModule(ActionBase):
101112
def run(self, tmp=None, task_vars=None):
102113
result = super(ActionModule, self).run(tmp, task_vars)
103114
del tmp
115+
104116
# ********************************************************** #
105117
# Parameter initializations #
106118
# ********************************************************** #
@@ -139,12 +151,60 @@ def run(self, tmp=None, task_vars=None):
139151
return result
140152

141153
ds_type = None
142-
fetch_member = "(" in src and src.endswith(")")
154+
fetch_member = data_set.is_member(src)
143155
if fetch_member:
144156
member_name = src[src.find("(") + 1: src.find(")")]
145157
src = self._connection._shell.join_path(src)
146158
src = self._remote_expand_user(src)
147159

160+
# ********************************************************** #
161+
# Execute module on remote host #
162+
# ********************************************************** #
163+
new_module_args = self._task.args.copy()
164+
encoding_to = None
165+
if encoding:
166+
encoding_to = encoding.get("to", None)
167+
if encoding is None or encoding_to is None:
168+
new_module_args.update(
169+
dict(encoding=dict(to=encode.Defaults.get_default_system_charset()))
170+
)
171+
remote_path = None
172+
173+
try:
174+
fetch_res = self._execute_module(
175+
module_name="ibm.ibm_zos_core.zos_fetch",
176+
module_args=new_module_args,
177+
task_vars=task_vars
178+
)
179+
ds_type = fetch_res.get("ds_type")
180+
src = fetch_res.get("file")
181+
remote_path = fetch_res.get("remote_path")
182+
183+
if fetch_res.get("msg"):
184+
result["msg"] = fetch_res.get("msg")
185+
result["stdout"] = fetch_res.get("stdout") or fetch_res.get(
186+
"module_stdout"
187+
)
188+
result["stderr"] = fetch_res.get("stderr") or fetch_res.get(
189+
"module_stderr"
190+
)
191+
result["stdout_lines"] = fetch_res.get("stdout_lines")
192+
result["stderr_lines"] = fetch_res.get("stderr_lines")
193+
result["rc"] = fetch_res.get("rc")
194+
result["failed"] = True
195+
return result
196+
197+
elif fetch_res.get("note"):
198+
result["note"] = fetch_res.get("note")
199+
return result
200+
201+
except Exception as err:
202+
result["msg"] = "Failure during module execution"
203+
result["stderr"] = str(err)
204+
result["stderr_lines"] = str(err).splitlines()
205+
result["failed"] = True
206+
return result
207+
148208
# ********************************************************** #
149209
# Determine destination path: #
150210
# 1. If the 'flat' parameter is 'false', then hostname #
@@ -216,52 +276,22 @@ def run(self, tmp=None, task_vars=None):
216276
local_checksum = _get_file_checksum(dest)
217277

218278
# ********************************************************** #
219-
# Execute module on remote host #
279+
# Fetch remote data.
220280
# ********************************************************** #
221-
new_module_args = self._task.args.copy()
222-
encoding_to = None
223-
if encoding:
224-
encoding_to = encoding.get("to", None)
225-
if encoding is None or encoding_to is None:
226-
new_module_args.update(
227-
dict(encoding=dict(to=encode.Defaults.get_default_system_charset()))
228-
)
229-
remote_path = None
230281
try:
231-
fetch_res = self._execute_module(
232-
module_name="ibm.ibm_zos_core.zos_fetch",
233-
module_args=new_module_args,
234-
task_vars=task_vars
235-
)
236-
ds_type = fetch_res.get("ds_type")
237-
src = fetch_res.get("file")
238-
remote_path = fetch_res.get("remote_path")
239-
240-
if fetch_res.get("msg"):
241-
result["msg"] = fetch_res.get("msg")
242-
result["stdout"] = fetch_res.get("stdout") or fetch_res.get(
243-
"module_stdout"
244-
)
245-
result["stderr"] = fetch_res.get("stderr") or fetch_res.get(
246-
"module_stderr"
247-
)
248-
result["stdout_lines"] = fetch_res.get("stdout_lines")
249-
result["stderr_lines"] = fetch_res.get("stderr_lines")
250-
result["rc"] = fetch_res.get("rc")
251-
result["failed"] = True
252-
return result
253-
254-
elif fetch_res.get("note"):
255-
result["note"] = fetch_res.get("note")
256-
return result
257-
258282
if ds_type in SUPPORTED_DS_TYPES:
259283
if ds_type == "PO" and os.path.isfile(dest) and not fetch_member:
260284
result[
261285
"msg"
262286
] = "Destination must be a directory to fetch a partitioned data set"
263287
result["failed"] = True
264288
return result
289+
if ds_type == "GDG" and os.path.isfile(dest):
290+
result[
291+
"msg"
292+
] = "Destination must be a directory to fetch a generation data group"
293+
result["failed"] = True
294+
return result
265295

266296
fetch_content = self._transfer_remote_content(
267297
dest,
@@ -272,7 +302,7 @@ def run(self, tmp=None, task_vars=None):
272302
if fetch_content.get("msg"):
273303
return fetch_content
274304

275-
if validate_checksum and ds_type != "PO" and not is_binary:
305+
if validate_checksum and ds_type != "GDG" and ds_type != "PO" and not is_binary:
276306
new_checksum = _get_file_checksum(dest)
277307
result["changed"] = local_checksum != new_checksum
278308
result["checksum"] = new_checksum
@@ -286,6 +316,7 @@ def run(self, tmp=None, task_vars=None):
286316
)
287317
result["failed"] = True
288318
return result
319+
289320
except Exception as err:
290321
result["msg"] = "Failure during module execution"
291322
result["stderr"] = str(err)
@@ -311,7 +342,7 @@ def _transfer_remote_content(
311342
result = dict()
312343
_sftp_action = 'get'
313344

314-
if src_type == "PO":
345+
if src_type == "PO" or src_type == "GDG":
315346
_sftp_action += ' -r' # add '-r` to clone the source trees
316347

317348
# To support multiple Ansible versions we must do some version detection and act accordingly
@@ -404,6 +435,6 @@ def _remote_cleanup(self, remote_path, src_type, encoding):
404435
# do not remove the original file.
405436
if not (src_type == "USS" and not encoding):
406437
rm_cmd = "rm -r {0}".format(remote_path)
407-
if src_type != "PO":
438+
if src_type != "PO" and src_type != "GDG":
408439
rm_cmd = rm_cmd.replace(" -r", "")
409440
self._connection.exec_command(rm_cmd)

plugins/module_utils/data_set.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -540,8 +540,8 @@ def data_set_type(name, volume=None):
540540
volume (str) -- The volume the data set may reside on.
541541
542542
Returns:
543-
str -- The type of the data set (one of "PS", "PO", "DA", "KSDS",
544-
"ESDS", "LDS" or "RRDS").
543+
str -- The type of the data set (one of "PS", "PO", "DA", "GDG",
544+
"KSDS", "ESDS", "LDS" or "RRDS").
545545
None -- If the data set does not exist or ZOAU is not able to determine
546546
the type.
547547
"""
@@ -551,10 +551,15 @@ def data_set_type(name, volume=None):
551551
data_sets_found = datasets.list_datasets(name)
552552

553553
# Using the organization property when it's a sequential or partitioned
554-
# dataset. VSAMs are not found by datasets.list_datasets.
554+
# dataset. VSAMs and GDGs are not found by datasets.list_datasets.
555555
if len(data_sets_found) > 0:
556556
return data_sets_found[0].organization
557557

558+
# Now trying to list GDGs through gdgs.
559+
data_sets_found = gdgs.list_gdg_names(name)
560+
if len(data_sets_found) > 0:
561+
return "GDG"
562+
558563
# Next, trying to get the DATA information of a VSAM through
559564
# LISTCAT.
560565
output = DataSet._get_listcat_data(name)

0 commit comments

Comments
 (0)