Skip to content

Commit 163b7e5

Browse files
[Enabler][1376]lineinfile_blockinfile_gdg/gds_and_special_character_support (#1516)
* Add support * Add test case * Changes for gdgs * Add test case, and fix for gdg and special characters * Fix sanity * Add fragment * Correct conditional * Fix absent test * Adjust check * Fix bad test case * Fix sanity and test cases * Fix unusual names * Fix new response * Fix sanity * Move validation of name * Resolve comments * Remove variables * Add examples of use of gds * Update zos_lineinfile.py * Add comments * Update plugins/modules/zos_blockinfile.py Co-authored-by: Fernando Flores <[email protected]> * Update plugins/modules/zos_blockinfile.py Co-authored-by: Fernando Flores <[email protected]> * Last chance --------- Co-authored-by: Fernando Flores <[email protected]>
1 parent 9740f9e commit 163b7e5

File tree

7 files changed

+548
-55
lines changed

7 files changed

+548
-55
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
minor_changes:
2+
- zos_lineinfile - Added support for GDG and GDS relative name notation to use a data set.
3+
And backup in new generations. Added support for data set names with special characters
4+
like $, /#, /- and @.
5+
(https://github.com/ansible-collections/ibm_zos_core/pull/1516).
6+
- zos_blockinfile - Added support for GDG and GDS relative name notation to use a data set.
7+
And backup in new generations. Added support for data set names with special characters
8+
like $, /#, /- and @.
9+
(https://github.com/ansible-collections/ibm_zos_core/pull/1516).

plugins/module_utils/backup.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,10 @@ def mvs_file_backup(dsn, bk_dsn=None, tmphlq=None):
9595
dsn = _validate_data_set_name(dsn).upper()
9696
if is_member(dsn):
9797
# added the check for a sub-mmember, just in this case
98-
if not bk_dsn:
99-
bk_dsn = extract_dsname(dsn) + "({0})".format(temp_member_name())
100-
elif "(" not in bk_dsn:
98+
if not bk_dsn or "(" not in bk_dsn:
10199
bk_dsn = extract_dsname(dsn) + "({0})".format(temp_member_name())
100+
elif DataSet.is_gds_positive_relative_name(bk_dsn):
101+
bk_dsn = datasets.create(bk_dsn)
102102

103103
bk_dsn = _validate_data_set_name(bk_dsn).upper()
104104
try:
@@ -128,7 +128,10 @@ def mvs_file_backup(dsn, bk_dsn=None, tmphlq=None):
128128
except exceptions.ZOAUException as copy_exception:
129129
cp_rc = copy_exception.response.rc
130130
else:
131-
cp_rc = _copy_ds(dsn, bk_dsn)
131+
if DataSet.is_gds_positive_relative_name(bk_dsn):
132+
cp_rc = datasets.copy(dsn, bk_dsn)
133+
else:
134+
cp_rc = _copy_ds(dsn, bk_dsn)
132135

133136
if cp_rc == 12: # The data set is probably a PDS or PDSE
134137
# Delete allocated backup that was created when attempting to use _copy_ds()
@@ -242,8 +245,8 @@ def _copy_ds(ds, bk_ds):
242245
module = AnsibleModuleHelper(argument_spec={})
243246
_allocate_model(bk_ds, ds)
244247
repro_cmd = """ REPRO -
245-
INDATASET({0}) -
246-
OUTDATASET({1})""".format(
248+
INDATASET('{0}') -
249+
OUTDATASET('{1}')""".format(
247250
ds, bk_ds
248251
)
249252
rc, out, err = module.run_command(

plugins/module_utils/data_set.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1345,6 +1345,23 @@ def is_gds_relative_name(name):
13451345
match = re.fullmatch(pattern, name)
13461346
return bool(match)
13471347

1348+
@staticmethod
1349+
def is_gds_positive_relative_name(name):
1350+
"""Determine if name is a gdg relative positive name
1351+
based on the GDS relative name syntax eg. 'USER.GDG(+1)'.
1352+
Parameters
1353+
----------
1354+
name : str
1355+
Data set name to determine if is a GDS relative name.
1356+
Returns
1357+
-------
1358+
bool
1359+
Whether the name is a GDS positive relative name.
1360+
"""
1361+
pattern = r'(.+)\(([\\]?[+]\d+)\)'
1362+
match = re.fullmatch(pattern, name)
1363+
return bool(match)
1364+
13481365
@staticmethod
13491366
def resolve_gds_absolute_name(relative_name):
13501367
"""Given a GDS relative name, returns its absolute name.
@@ -1727,6 +1744,7 @@ def _gather_data_set_info(self):
17271744
dict -- Dictionary containing data set attributes
17281745
"""
17291746
result = dict()
1747+
self.data_set = self.data_set.upper().replace("\\", '')
17301748
listds_rc, listds_out, listds_err = mvs_cmd.ikjeft01(
17311749
" LISTDS '{0}'".format(self.data_set), authorized=True
17321750
)

plugins/modules/zos_blockinfile.py

Lines changed: 108 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
- The location can be a UNIX System Services (USS) file,
3939
PS (sequential data set), member of a PDS or PDSE, PDS, PDSE.
4040
- The USS file must be an absolute pathname.
41+
- Generation data set (GDS) relative name of generation already
42+
created. C(e.g. SOME.CREATION(-1).)
4143
type: str
4244
aliases: [ path, destfile, name ]
4345
required: true
@@ -110,6 +112,7 @@
110112
- When set to C(true), the module creates a backup file or data set.
111113
- The backup file name will be returned on either success or failure of
112114
module execution such that data can be retrieved.
115+
- Use generation data set (GDS) relative positive name. C(e.g. SOME.CREATION(+1))
113116
required: false
114117
type: bool
115118
default: false
@@ -279,6 +282,20 @@
279282
marker_begin: "Begin Ansible Block Insertion 2"
280283
marker_end: "End Ansible Block Insertion 2"
281284
block: "{{ CONTENT }}"
285+
286+
- name: Add a block to a gds
287+
zos_blockinfile:
288+
src: TEST.SOME.CREATION(0)
289+
insertafter: EOF
290+
block: "{{ CONTENT }}"
291+
292+
- name: Add a block to dataset and backup in a new generation of gds
293+
zos_blockinfile:
294+
src: SOME.CREATION.TEST
295+
insertbefore: BOF
296+
backup: True
297+
backup_name: CREATION.GDS(+1)
298+
block: "{{ CONTENT }}"
282299
'''
283300

284301
RETURN = r"""
@@ -456,13 +473,44 @@ def quotedString(string):
456473

457474

458475
def quotedString_double_quotes(string):
476+
"""Deletes the quote mark on strings.
477+
478+
Parameters
479+
----------
480+
string : str
481+
String to modify quote marks from.
482+
483+
Returns
484+
-------
485+
str
486+
String scaping the quote marks.
487+
"""
459488
# add escape if string was quoted
460489
if not isinstance(string, str):
461490
return string
462491
return string.replace('"', '\\"')
463492

464493

465494
def check_double_quotes(marker, ins_bef, ins_aft, block):
495+
"""Verify the content of strings to determine if double
496+
quotes are in the string.
497+
498+
Parameters
499+
----------
500+
marker : str
501+
String to verify quote marks from.
502+
ins_bef : str
503+
String to verify quote marks from.
504+
ins_aft : str
505+
String to verify quote marks from.
506+
block : str
507+
String to verify quote marks from.
508+
509+
Returns
510+
-------
511+
bool
512+
If any string contain double quotes.
513+
"""
466514
if marker:
467515
if '"' in marker:
468516
return True
@@ -479,6 +527,42 @@ def check_double_quotes(marker, ins_bef, ins_aft, block):
479527

480528

481529
def execute_dmod(src, block, marker, force, encoding, state, module, ins_bef=None, ins_aft=None):
530+
"""Execute in terminal dmod command directly.
531+
532+
Parameters
533+
----------
534+
src : str
535+
The z/OS USS file or data set to modify.
536+
block : str
537+
The block to insert/replace into the src.
538+
marker : str
539+
The block will be inserted/updated with the markers.
540+
force : bool
541+
If not empty passes True option to dmod cmd.
542+
encoding : str
543+
Encoding of the src.
544+
state : bool
545+
Determine if will add or delete the block.
546+
module : obj
547+
Object to execute the command.
548+
ins_bef : str
549+
Insert the block before matching '*regex*' pattern or BOF.
550+
choices:
551+
- BOF
552+
- '*regex*'
553+
ins_aft : str
554+
Insert the block after matching '*regex*' pattern or EOF.
555+
choices:
556+
- EOF
557+
- '*regex*'
558+
559+
Returns
560+
-------
561+
int
562+
RC of the execution of the command.
563+
cmd
564+
Command executed.
565+
"""
482566
block = block.replace('"', '\\"')
483567
force = "-f" if force else ""
484568
encoding = "-c {0}".format(encoding) if encoding else ""
@@ -505,6 +589,18 @@ def execute_dmod(src, block, marker, force, encoding, state, module, ins_bef=Non
505589

506590

507591
def clean_command(cmd):
592+
"""Deletes escaped characters from the str.
593+
594+
Parameters
595+
----------
596+
cmd : str
597+
Command to clean any escaped characters.
598+
599+
Returns
600+
-------
601+
str
602+
Command without escaped characters.
603+
"""
508604
cmd = cmd.replace('/c\\\\', '')
509605
cmd = cmd.replace('/a\\\\', '', )
510606
cmd = cmd.replace('/i\\\\', '', )
@@ -667,18 +763,25 @@ def main():
667763
marker = "{0}\\n{1}\\n{2}".format(marker_begin, marker_end, marker)
668764
block = transformBlock(block, ' ', indentation)
669765
# analysis the file type
766+
if "/" not in src:
767+
dataset = data_set.MVSDataSet(
768+
name=src
769+
)
770+
src = dataset.name
771+
772+
if data_set.DataSet.is_gds_relative_name(src):
773+
module.fail_json(msg="{0} does not exist".format(src))
774+
670775
ds_utils = data_set.DataSetUtils(src)
671776
if not ds_utils.exists():
672777
message = "{0} does NOT exist".format(str(src))
673778
module.fail_json(msg=message)
674779
file_type = ds_utils.ds_type()
675-
if file_type == 'USS':
676-
file_type = 1
677-
else:
780+
781+
if file_type != "USS":
678782
if file_type not in DS_TYPE:
679783
message = "{0} data set type is NOT supported".format(str(file_type))
680784
module.fail_json(msg=message)
681-
file_type = 0
682785

683786
return_content = None
684787
if backup:
@@ -688,7 +791,7 @@ def main():
688791
if isinstance(backup, bool):
689792
backup = None
690793
try:
691-
if file_type:
794+
if file_type == "USS":
692795
result['backup_name'] = Backup.uss_file_backup(src, backup_name=backup, compress=False)
693796
else:
694797
result['backup_name'] = Backup.mvs_file_backup(dsn=src, bk_dsn=backup, tmphlq=tmphlq)

0 commit comments

Comments
 (0)