Skip to content

Commit a58ddce

Browse files
authored
When zos_blockinfile tries to introduce a block with double quotes generates a false negative (#1933)
* code changes for blockinfile * adding changelog and year change * Review comments incorporation
1 parent 92fdf18 commit a58ddce

File tree

3 files changed

+26
-204
lines changed

3 files changed

+26
-204
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
trivial:
2+
- zos_blockinfile - Previously, double quotes were being handled separately as it was not supported by ZOAU.
3+
Fix now removes the redundant code as ZOAU handles double quotes as expected now.
4+
(https://github.com/ansible-collections/ibm_zos_core/pull/1933).

plugins/modules/zos_blockinfile.py

Lines changed: 21 additions & 203 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, 2024
4+
# Copyright (c) IBM Corporation 2020, 2025
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
@@ -349,7 +349,7 @@
349349
from ansible_collections.ibm.ibm_zos_core.plugins.module_utils import (
350350
better_arg_parser, data_set, backup as Backup)
351351
from ansible_collections.ibm.ibm_zos_core.plugins.module_utils.import_handler import (
352-
ZOAUImportError,
352+
ZOAUImportError
353353
)
354354

355355
try:
@@ -455,171 +455,6 @@ def absent(src, marker, encoding, force):
455455
return datasets.blockinfile(src, False, marker=marker, encoding=encoding, force=force, as_json=True)
456456

457457

458-
def quotedString(string):
459-
"""Deletes the quote mark on strings.
460-
461-
Parameters
462-
----------
463-
string : str
464-
String to delete quote marks from.
465-
466-
Returns
467-
-------
468-
str
469-
String without the quote marks.
470-
"""
471-
# add escape if string was quoted
472-
if not isinstance(string, str):
473-
return string
474-
return string.replace('"', "")
475-
476-
477-
def quotedString_double_quotes(string):
478-
"""Deletes the quote mark on strings.
479-
480-
Parameters
481-
----------
482-
string : str
483-
String to modify quote marks from.
484-
485-
Returns
486-
-------
487-
str
488-
String scaping the quote marks.
489-
"""
490-
# add escape if string was quoted
491-
if not isinstance(string, str):
492-
return string
493-
return string.replace('"', '\\"')
494-
495-
496-
def check_double_quotes(marker, ins_bef, ins_aft, block):
497-
"""Verify the content of strings to determine if double
498-
quotes are in the string.
499-
500-
Parameters
501-
----------
502-
marker : str
503-
String to verify quote marks from.
504-
ins_bef : str
505-
String to verify quote marks from.
506-
ins_aft : str
507-
String to verify quote marks from.
508-
block : str
509-
String to verify quote marks from.
510-
511-
Returns
512-
-------
513-
bool
514-
If any string contain double quotes.
515-
"""
516-
if marker:
517-
if '"' in marker:
518-
return True
519-
if ins_bef:
520-
if '"' in ins_bef:
521-
return True
522-
if ins_aft:
523-
if '"' in ins_aft:
524-
return True
525-
if block:
526-
if '"' in block:
527-
return True
528-
return False
529-
530-
531-
def execute_dmod(src, block, marker, force, encoding, state, module, ins_bef=None, ins_aft=None):
532-
"""Execute in terminal dmod command directly.
533-
534-
Parameters
535-
----------
536-
src : str
537-
The z/OS USS file or data set to modify.
538-
block : str
539-
The block to insert/replace into the src.
540-
marker : str
541-
The block will be inserted/updated with the markers.
542-
force : bool
543-
If not empty passes True option to dmod cmd.
544-
encoding : str
545-
Encoding of the src.
546-
state : bool
547-
Determine if will add or delete the block.
548-
module : obj
549-
Object to execute the command.
550-
ins_bef : str
551-
Insert the block before matching '*regex*' pattern or BOF.
552-
choices:
553-
- BOF
554-
- '*regex*'
555-
ins_aft : str
556-
Insert the block after matching '*regex*' pattern or EOF.
557-
choices:
558-
- EOF
559-
- '*regex*'
560-
561-
Returns
562-
-------
563-
int
564-
RC of the execution of the command.
565-
cmd
566-
Command executed.
567-
"""
568-
block = block.replace('"', '\\"')
569-
force = "-f" if force else ""
570-
encoding = "-c {0}".format(encoding) if encoding else ""
571-
marker = "-m \"{0}\"".format(marker) if marker else ""
572-
if state:
573-
if ins_aft:
574-
if ins_aft == "EOF":
575-
opts = f'"$ a\\{block}" "{src}"'
576-
else:
577-
opts = f'-s -e "/{ins_aft}/a\\{block}/$" -e "$ a\\{block}" "{src}"'
578-
elif ins_bef:
579-
if ins_bef == "BOF":
580-
opts = f' "1 i\\{block}" "{src}" '
581-
else:
582-
opts = f'-s -e "/{ins_bef}/i\\{block}/$" -e "$ a\\{block}" "{src}"'
583-
584-
cmd = "dmod -b {0} {1} {2} {3}".format(force, encoding, marker, opts)
585-
else:
586-
cmd = """dmod -b {0} {1} {2} {3}""".format(force, encoding, marker, src)
587-
588-
rc, stdout, stderr = module.run_command(cmd, errors='replace')
589-
cmd = clean_command(cmd)
590-
return rc, cmd
591-
592-
593-
def clean_command(cmd):
594-
"""Deletes escaped characters from the str.
595-
596-
Parameters
597-
----------
598-
cmd : str
599-
Command to clean any escaped characters.
600-
601-
Returns
602-
-------
603-
str
604-
Command without escaped characters.
605-
"""
606-
cmd = cmd.replace('/c\\\\', '')
607-
cmd = cmd.replace('/a\\\\', '', )
608-
cmd = cmd.replace('/i\\\\', '', )
609-
cmd = cmd.replace('$ a\\\\', '', )
610-
cmd = cmd.replace('1 i\\\\', '', )
611-
cmd = cmd.replace('/c\\', '')
612-
cmd = cmd.replace('/a\\', '')
613-
cmd = cmd.replace('/i\\', '')
614-
cmd = cmd.replace('$ a\\', '')
615-
cmd = cmd.replace('1 i\\', '')
616-
cmd = cmd.replace('/d', '')
617-
cmd = cmd.replace('\\\\d', '')
618-
cmd = cmd.replace('\\n', '\n')
619-
cmd = cmd.replace('\\"', '"')
620-
return cmd
621-
622-
623458
def main():
624459
"""Run the zos_blockinfile module core functions.
625460
@@ -800,47 +635,30 @@ def main():
800635
result['backup_name'] = Backup.mvs_file_backup(dsn=src, bk_dsn=backup, tmphlq=tmphlq)
801636
except Exception as err:
802637
module.fail_json(msg="Unable to allocate backup {0} destination: {1}".format(backup, str(err)))
803-
double_quotes_exists = check_double_quotes(marker, ins_bef, ins_aft, block)
804638
# state=present, insert/replace a block with matching regex pattern
805639
# state=absent, delete blocks with matching regex pattern
806640
if parsed_args.get('state') == 'present':
807-
if double_quotes_exists:
808-
rc, cmd = execute_dmod(src, block, quotedString_double_quotes(marker), force, encoding, True, module=module,
809-
ins_bef=quotedString_double_quotes(ins_bef), ins_aft=quotedString_double_quotes(ins_aft))
810-
result['rc'] = rc
811-
result['cmd'] = cmd
812-
result['changed'] = True if rc == 0 else False
813-
stderr = 'Failed to insert new entry' if rc != 0 else ""
814-
else:
815-
return_content = present(src, block, marker, ins_aft, ins_bef, encoding, force)
641+
return_content = present(src, block, marker, ins_aft, ins_bef, encoding, force)
816642
else:
817-
if double_quotes_exists:
818-
rc, cmd = execute_dmod(src, block, quotedString_double_quotes(marker), force, encoding, False, module=module)
819-
result['rc'] = rc
820-
result['cmd'] = cmd
821-
result['changed'] = True if rc == 0 else False
822-
stderr = 'Failed to remove entry' if rc != 0 else ""
823-
else:
824-
return_content = absent(src, marker, encoding, force)
643+
return_content = absent(src, marker, encoding, force)
825644
# ZOAU 1.3.0 generate false positive working with double quotes (") the call generate distinct return when using and not
826-
if not double_quotes_exists:
827-
stdout = return_content.stdout_response
828-
stderr = return_content.stderr_response
829-
rc = return_content.rc
830-
stdout = stdout.replace('/d', '\\\\d')
831-
try:
832-
# Try to extract information from stdout
833-
# The triple double quotes is required for special characters (/_) been scape
834-
ret = json.loads("""{0}""".format(stdout))
835-
except Exception:
836-
messageDict = dict(msg="ZOAU dmod return content is NOT in json format", stdout=str(stdout), stderr=str(stderr), rc=rc)
837-
if result.get('backup_name'):
838-
messageDict['backup_name'] = result['backup_name']
839-
module.fail_json(**messageDict)
840-
841-
result['cmd'] = ret['data']['commands']
842-
result['changed'] = ret['data']['changed']
843-
result['found'] = ret['data']['found']
645+
stdout = return_content.stdout_response
646+
stderr = return_content.stderr_response
647+
rc = return_content.rc
648+
stdout = stdout.replace('/d', '\\\\d')
649+
try:
650+
# Try to extract information from stdout
651+
# The triple double quotes is required for special characters (/_) been scape
652+
ret = json.loads("""{0}""".format(stdout))
653+
except Exception:
654+
messageDict = dict(msg="ZOAU dmod return content is NOT in json format", stdout=str(stdout), stderr=str(stderr), rc=rc)
655+
if result.get('backup_name'):
656+
messageDict['backup_name'] = result['backup_name']
657+
module.fail_json(**messageDict)
658+
659+
result['cmd'] = ret['data']['commands']
660+
result['changed'] = ret['data']['changed']
661+
result['found'] = ret['data']['found']
844662
# Only return 'rc' if stderr is not empty to not fail the playbook run in a nomatch case
845663
# That information will be given with 'changed' and 'found'
846664
if len(stderr):

tests/functional/modules/test_zos_blockinfile_func.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# -*- coding: utf-8 -*-
22

3-
# Copyright (c) IBM Corporation 2020, 2024
3+
# Copyright (c) IBM Corporation 2020, 2025
44
# Licensed under the Apache License, Version 2.0 (the "License");
55
# you may not use this file except in compliance with the License.
66
# You may obtain a copy of the License at

0 commit comments

Comments
 (0)