Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add force (disp=shr) option to zos_lineinfile and update zos_blockinfile with the same locking test case. #731

Merged
merged 28 commits into from
Apr 24, 2023
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions changelogs/fragments/731-zos_linefile-disposition_share.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
minor_changes:
- zos_lineinfile - would access data sets with exclusive access so no other
task can read the data, this enhancement allows for a data set to be opened
with a disposition set to share so that other tasks can access the data when
option `force` is set to `true`.
(https://github.com/ansible-collections/ibm_zos_core/pull/731)
39 changes: 33 additions & 6 deletions plugins/modules/zos_lineinfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,18 @@
required: false
type: str
default: IBM-1047
force:
description:
- Specifies that the data set can be shared with others during an update
which results in the data set you are updating to be simultaneously
updated by others.
- This is helpful when a data set is being used in a long running process
such as a started task and you are wanting to update or read.
- The C(force) option enables sharing of data sets through the disposition
I(DISP=SHR).
required: false
type: bool
default: false
notes:
- It is the playbook author or user's responsibility to avoid files
that should not be encoded, such as binary files. A user is described
Expand Down Expand Up @@ -218,6 +230,14 @@
regexp: '^(.*)User(\d+)m(.*)$'
line: '\1APPUser\3'
backrefs: yes

- name: Add a line to a member while a task is in execution
zos_lineinfile:
src: "{{ DATASET_NAME }}(MEM3)"
insertafter: EOF
line: 'Should be a working test now'
force: True

"""

RETURN = r"""
Expand Down Expand Up @@ -271,7 +291,7 @@
DS_TYPE = ['PS', 'PO']


def present(src, line, regexp, ins_aft, ins_bef, encoding, first_match, backrefs):
def present(src, line, regexp, ins_aft, ins_bef, encoding, first_match, backrefs, force):
"""Replace a line with the matching regex pattern
Insert a line before/after the matching pattern
Insert a line at BOF/EOF
Expand All @@ -292,6 +312,7 @@ def present(src, line, regexp, ins_aft, ins_bef, encoding, first_match, backrefs
encoding: {str} -- Encoding of the src.
first_match: {bool} -- Take the first matching regex pattern.
backrefs: {bool} -- Back reference
force: {bool} -- force for modify a member part of a task in execution

Returns:
str -- Information in JSON format. keys:
Expand All @@ -310,10 +331,11 @@ def present(src, line, regexp, ins_aft, ins_bef, encoding, first_match, backrefs
backref=backrefs,
state=True,
debug=True,
force=force,
)


def absent(src, line, regexp, encoding):
def absent(src, line, regexp, encoding, force):
"""Delete lines with matching regex pattern

Arguments:
Expand All @@ -322,14 +344,15 @@ def absent(src, line, regexp, encoding):
regexp will be ignored.
regexp: {str} -- The regular expression to look for in every line of the src.
encoding: {str} -- Encoding of the src.
force: {bool} -- force for modify a member part of a task in execution

Returns:
str -- Information in JSON format. keys:
cmd: {str} -- dsed shell command
found: {int} -- Number of matching regex pattern
changed: {bool} -- Indicates if the source was modified.
"""
return datasets.lineinfile(src, line, regex=regexp, encoding=encoding, state=False, debug=True)
return datasets.lineinfile(src, line, regex=regexp, encoding=encoding, state=False, debug=True, force=force)


def quotedString(string):
Expand Down Expand Up @@ -364,7 +387,8 @@ def main():
backup_name=dict(type='str', required=False, default=None),
firstmatch=dict(type='bool', default=False),
encoding=dict(type='str', default="IBM-1047"),
tmp_hlq=dict(type='str', required=False, default=None)
tmp_hlq=dict(type='str', required=False, default=None),
force=dict(type='bool', default=False)
)
module = AnsibleModule(
argument_spec=module_args,
Expand All @@ -385,6 +409,7 @@ def main():
firstmatch=dict(arg_type="bool", required=False, default=False),
backrefs=dict(arg_type="bool", dependencies=['regexp'], required=False, default=False),
tmp_hlq=dict(type='qualifier_or_empty', required=False, default=None),
force=dict(arg_type='bool', required=False, default=False),
mutually_exclusive=[["insertbefore", "insertafter"]],)

try:
Expand All @@ -406,6 +431,7 @@ def main():
ins_bef = parsed_args.get('insertbefore')
encoding = parsed_args.get('encoding')
tmphlq = parsed_args.get('tmp_hlq')
force = parsed_args.get('force')

if parsed_args.get('state') == 'present':
if backrefs and regexp is None:
Expand Down Expand Up @@ -453,9 +479,10 @@ def main():
# state=present, insert/replace a line with matching regex pattern
# state=absent, delete lines with matching regex pattern
if parsed_args.get('state') == 'present':
return_content = present(src, quotedString(line), quotedString(regexp), quotedString(ins_aft), quotedString(ins_bef), encoding, firstmatch, backrefs)
return_content = present(src, quotedString(line), quotedString(regexp), quotedString(ins_aft), quotedString(ins_bef), encoding, firstmatch,
backrefs, force)
else:
return_content = absent(src, quotedString(line), quotedString(regexp), encoding)
return_content = absent(src, quotedString(line), quotedString(regexp), encoding, force)
stdout = return_content.stdout_response
stderr = return_content.stderr_response
rc = return_content.rc
Expand Down
35 changes: 33 additions & 2 deletions tests/functional/modules/test_zos_blockinfile_func.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
DsGeneral,
DsNotSupportedHelper,
DsGeneralResultKeyMatchesRegex,
DsGeneralForce,
DsGeneralForceFail,
)
import os
import sys
Expand Down Expand Up @@ -238,6 +240,14 @@
test_ds_block_insertafter_eof_with_backup_name=dict(
block="export ZOAU_ROOT\nexport ZOAU_HOME\nexport ZOAU_DIR",
state="present", backup=True, backup_name=MVS_BACKUP_DS),
test_ds_block_insertafter_regex_force=dict(
path="",insertafter="ZOAU_ROOT=",
block="ZOAU_ROOT=/mvsutil-develop_dsed\nZOAU_HOME=\\$ZOAU_ROOT\nZOAU_DIR=\\$ZOAU_ROOT",
state="present", force=True),
test_ds_block_insertafter_regex_force_fail=dict(
path="",insertafter="ZOAU_ROOT=",
block="ZOAU_ROOT=/mvsutil-develop_dsed\nZOAU_HOME=\\$ZOAU_ROOT\nZOAU_DIR=\\$ZOAU_ROOT",
state="present", force=False),
expected=dict(test_uss_block_insertafter_regex_defaultmarker="""if [ -z STEPLIB ] && tty -s;
then
export STEPLIB=none
Expand Down Expand Up @@ -1238,7 +1248,7 @@ def test_uss_block_insertafter_eof_with_backup_name(ansible_zos_module):
for result in results.contacted.values():
assert result.get("stdout") == TEST_ENV["TEST_CONT"]
finally:
ansible_zos_module.all.file(path=USS_BACKUP_FILE, state="absent")
ansible_zos_module.all.file(path=USS_BACKUP_FILE, state="absent")


#########################
Expand Down Expand Up @@ -1498,6 +1508,17 @@ def test_ds_block_insertafter_eof_with_backup(ansible_zos_module, dstype, encodi
ansible_zos_module.all.zos_data_set(name=backup_ds_name, state="absent")


#@pytest.mark.ds
#@pytest.mark.parametrize("dstype", DS_TYPE)
#def test_ds_block_insertafter_regex_force(ansible_zos_module, dstype):
# TEST_ENV["DS_TYPE"] = dstype
# DsGeneralForce(
# ansible_zos_module, TEST_ENV,
# TEST_INFO["test_ds_block_insertafter_regex_force"],
# TEST_INFO["expected"]["test_uss_block_insertafter_regex_defaultmarker"]
# )


#########################
# Negative tests
#########################
Expand Down Expand Up @@ -1545,4 +1566,14 @@ def test_ds_not_supported(ansible_zos_module, dstype):
DsNotSupportedHelper(
TEST_INFO["test_ds_block_insertafter_regex"]["test_name"], ansible_zos_module,
TEST_ENV, TEST_INFO["test_uss_block_insertafter_regex"]
)
)


@pytest.mark.ds
@pytest.mark.parametrize("dstype", DS_TYPE)
def test_ds_block_insertafter_regex_fail(ansible_zos_module, dstype):
TEST_ENV["DS_TYPE"] = dstype
DsGeneralForceFail(
ansible_zos_module, TEST_ENV,
TEST_INFO["test_ds_block_insertafter_regex_force_fail"],
)
Loading