Skip to content

Commit 24d693a

Browse files
[zos_find] Support GDG and special characters in data set names for zos_find (#1518)
* initial fragment and added test case * added changelog file modified new test, added limit to test value * needed to add limit parameter for gdg in the batch suboption of data_set * added limit as a dependancy to batch/datasettpe * added detail to the error raised on missing limit * changing test to eliminate batch call * fixed parenthesis * update test constructor to create gds (no paren) and use force=true * detailing the decho response * trying to escape the gdg number * updated zos_data_set definition for batch mode of gdg in dataset altered test to re-test the new definition * fixed parenthesis issue * data set creator is now working. removed escapes around gdg version indicator in test. * expanded test to write content generation entry * updating test to remove prior output and truncate the final version info * removed cars print statement. * corrected join statement in string split * updated join statement * added cleanup to gdg test routine * adding detail to pds writer * updated test updated for specific file type, not just top 2 names * added special character tests for zos_find * changed an assertion in special/symbol test * added force=True to gdg deletion in that test, so the subs would all get removed * added escape to decho command * updated changelog fragment to include PR number removed print statements from test * added dataset import back into test function * working on re-establishing zos_data_set in the test form * correcting import for zos_data_set * Modified test cases to use random generated names and improve performance * Removed hard coded names * Adding support for GDG filter in zos_find * Modified tests for filtering GDGs * Added documentation samples and fixed sanity issues * fixed test suite --------- Co-authored-by: Fernando Flores <[email protected]>
1 parent 48b10af commit 24d693a

File tree

3 files changed

+253
-21
lines changed

3 files changed

+253
-21
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_find - added support for GDG/GDS and special characters
3+
(https://github.com/ansible-collections/ibm_zos_core/pull/1518).

plugins/modules/zos_find.py

Lines changed: 147 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/python
22
# -*- coding: utf-8 -*-
33

4-
# Copyright (c) IBM Corporation 2020, 2023
4+
# Copyright (c) IBM Corporation 2020, 2024
55
# Licensed under the Apache License, Version 2.0 (the "License");
66
# you may not use this file except in compliance with the License.
77
# You may obtain a copy of the License at
@@ -109,11 +109,13 @@
109109
- C(nonvsam) refers to one of SEQ, LIBRARY (PDSE), PDS, LARGE, BASIC, EXTREQ, or EXTPREF.
110110
- C(cluster) refers to a VSAM cluster. The C(data) and C(index) are the data and index
111111
components of a VSAM cluster.
112+
- C(gdg) refers to Generation Data Groups. The module searches based on the GDG base name.
112113
choices:
113114
- nonvsam
114115
- cluster
115116
- data
116117
- index
118+
- gdg
117119
type: str
118120
required: false
119121
default: "nonvsam"
@@ -126,6 +128,43 @@
126128
required: false
127129
aliases:
128130
- volumes
131+
empty:
132+
description:
133+
- A GDG attribute, only valid when C(resource_type=gdg).
134+
- If provided, will search for data sets with I(empty) attribute set as provided.
135+
type: bool
136+
required: false
137+
extended:
138+
description:
139+
- A GDG attribute, only valid when C(resource_type=gdg).
140+
- If provided, will search for data sets with I(extended) attribute set as provided.
141+
type: bool
142+
required: false
143+
fifo:
144+
description:
145+
- A GDG attribute, only valid when C(resource_type=gdg).
146+
- If provided, will search for data sets with I(fifo) attribute set as provided.
147+
type: bool
148+
required: false
149+
limit:
150+
description:
151+
- A GDG attribute, only valid when C(resource_type=gdg).
152+
- If provided, will search for data sets with I(limit) attribute set as provided.
153+
type: int
154+
required: false
155+
purge:
156+
description:
157+
- A GDG attribute, only valid when C(resource_type=gdg).
158+
- If provided, will search for data sets with I(purge) attribute set as provided.
159+
type: bool
160+
required: false
161+
scratch:
162+
description:
163+
- A GDG attribute, only valid when C(resource_type=gdg).
164+
- If provided, will search for data sets with I(scratch) attribute set as provided.
165+
type: bool
166+
required: false
167+
129168
notes:
130169
- Only cataloged data sets will be searched. If an uncataloged data set needs to
131170
be searched, it should be cataloged first. The L(zos_data_set,./zos_data_set.html) module can be
@@ -186,6 +225,16 @@
186225
patterns:
187226
- USER.*
188227
resource_type: cluster
228+
229+
- name: Find all Generation Data Groups starting with the word 'USER' and specific GDG attributes.
230+
zos_find:
231+
patterns:
232+
- USER.*
233+
resource_type: gdg
234+
limit: 30
235+
scratch: true
236+
purge: true
237+
189238
"""
190239

191240

@@ -249,6 +298,7 @@
249298
import time
250299
import datetime
251300
import math
301+
import json
252302

253303
from copy import deepcopy
254304
from re import match as fullmatch
@@ -535,6 +585,66 @@ def data_set_attribute_filter(
535585
return filtered_data_sets
536586

537587

588+
def gdg_filter(module, data_sets, limit, empty, fifo, purge, scratch, extended):
589+
""" Filter Generation Data Groups based on their attributes.
590+
591+
Parameters
592+
----------
593+
module : AnsibleModule
594+
The Ansible module object being used.
595+
data_sets : set[str]
596+
A set of data set names.
597+
limit : int
598+
The limit GDG attribute that should be used to filter GDGs.
599+
empty : bool
600+
The empty GDG attribute, that should be used to filter GDGs.
601+
fifo : bool
602+
The fifo GDG attribute, that should be used to filter GDGs.
603+
purge : bool
604+
The purge GDG attribute, that should be used to filter GDGs.
605+
scratch : bool
606+
The scratch GDG attribute, that should be used to filter GDGs.
607+
extended : bool
608+
The extended GDG attribute, that should be used to filter GDGs.
609+
610+
Returns
611+
-------
612+
set[str]
613+
Matched GDG base names.
614+
615+
Raises
616+
------
617+
fail_json
618+
Non-zero return code received while executing ZOAU shell command 'dls'.
619+
"""
620+
filtered_data_sets = set()
621+
for ds in data_sets:
622+
rc, out, err = _dls_wrapper(ds, data_set_type='gdg', list_details=True, json=True)
623+
624+
if rc != 0:
625+
module.fail_json(
626+
msg="Non-zero return code received while executing ZOAU shell command 'dls'",
627+
rc=rc, stdout=out, stderr=err
628+
)
629+
try:
630+
response = json.loads(out)
631+
gdgs = response['data']['gdgs']
632+
for gdg in gdgs:
633+
if (
634+
gdg['limit'] == (gdg['limit'] if limit is None else limit) and
635+
gdg['empty'] == (gdg['empty'] if empty is None else empty) and
636+
gdg['purge'] == (gdg['purge'] if purge is None else purge) and
637+
gdg['fifo'] == (gdg['fifo'] if fifo is None else fifo) and
638+
gdg['scratch'] == (gdg['scratch'] if scratch is None else scratch) and
639+
gdg['extended'] == (gdg['extended'] if extended is None else extended)
640+
):
641+
filtered_data_sets.add(gdg['base'])
642+
except Exception as e:
643+
module.fail_json(repr(e))
644+
645+
return filtered_data_sets
646+
647+
538648
# TODO:
539649
# Implement volume_filter() using "vtocls" shell command from ZOAU
540650
# when it becomes available.
@@ -779,7 +889,9 @@ def _dls_wrapper(
779889
u_time=False,
780890
size=False,
781891
verbose=False,
782-
migrated=False
892+
migrated=False,
893+
data_set_type="",
894+
json=False,
783895
):
784896
"""A wrapper for ZOAU 'dls' shell command.
785897
@@ -797,6 +909,10 @@ def _dls_wrapper(
797909
Display verbose information.
798910
migrated : bool
799911
Display migrated data sets.
912+
data_set_type : str
913+
Data set type to look for.
914+
json : bool
915+
Return content in json format.
800916
801917
Returns
802918
-------
@@ -815,6 +931,10 @@ def _dls_wrapper(
815931
dls_cmd += " -s"
816932
if verbose:
817933
dls_cmd += " -v"
934+
if data_set_type:
935+
dls_cmd += f" -t{data_set_type}"
936+
if json:
937+
dls_cmd += " -j"
818938

819939
dls_cmd += " {0}".format(quote(data_set_pattern))
820940
return AnsibleModuleHelper(argument_spec={}).run_command(dls_cmd)
@@ -926,6 +1046,12 @@ def run_module(module):
9261046
)
9271047
resource_type = module.params.get('resource_type').upper()
9281048
volume = module.params.get('volume') or module.params.get('volumes')
1049+
limit = module.params.get('limit')
1050+
empty = module.params.get('empty')
1051+
scratch = module.params.get('scratch')
1052+
purge = module.params.get('purge')
1053+
extended = module.params.get('extended')
1054+
fifo = module.params.get('fifo')
9291055

9301056
res_args = dict(data_sets=[])
9311057
filtered_data_sets = set()
@@ -983,9 +1109,11 @@ def run_module(module):
9831109

9841110
res_args['examined'] = init_filtered_data_sets.get("searched")
9851111

986-
else:
1112+
elif resource_type == "CLUSTER":
9871113
filtered_data_sets = vsam_filter(module, patterns, resource_type, age=age)
9881114
res_args['examined'] = len(filtered_data_sets)
1115+
elif resource_type == "GDG":
1116+
filtered_data_sets = gdg_filter(module, patterns, limit, empty, fifo, purge, scratch, extended)
9891117

9901118
# Filter out data sets that match one of the patterns in 'excludes'
9911119
if excludes and not pds_paths:
@@ -1045,14 +1173,20 @@ def main():
10451173
),
10461174
resource_type=dict(
10471175
type="str", required=False, default="nonvsam",
1048-
choices=["cluster", "data", "index", "nonvsam"]
1176+
choices=["cluster", "data", "index", "nonvsam", "gdg"]
10491177
),
10501178
volume=dict(
10511179
type="list",
10521180
elements="str",
10531181
required=False,
10541182
aliases=["volumes"]
1055-
)
1183+
),
1184+
limit=dict(type="int", required=False),
1185+
empty=dict(type="bool", required=False),
1186+
purge=dict(type="bool", required=False),
1187+
scratch=dict(type="bool", required=False),
1188+
extended=dict(type="bool", required=False),
1189+
fifo=dict(type="bool", required=False),
10561190
)
10571191
)
10581192

@@ -1077,9 +1211,15 @@ def main():
10771211
arg_type="str",
10781212
required=False,
10791213
default="nonvsam",
1080-
choices=["cluster", "data", "index", "nonvsam"]
1214+
choices=["cluster", "data", "index", "nonvsam", "gdg"]
10811215
),
1082-
volume=dict(arg_type="list", required=False, aliases=["volumes"])
1216+
volume=dict(arg_type="list", required=False, aliases=["volumes"]),
1217+
limit=dict(type="int", required=False),
1218+
empty=dict(type="bool", required=False),
1219+
purge=dict(type="bool", required=False),
1220+
scratch=dict(type="bool", required=False),
1221+
extended=dict(type="bool", required=False),
1222+
fifo=dict(type="bool", required=False),
10831223
)
10841224
try:
10851225
BetterArgParser(arg_def).parse_args(module.params)

0 commit comments

Comments
 (0)