Skip to content

Commit c589978

Browse files
Use datasets.write to write JCL instead of cp (#170)
Use datasets.write to write JCL instead of cp --------- Signed-off-by: Stewart Francis <[email protected]>
1 parent 520f5bf commit c589978

File tree

7 files changed

+162
-73
lines changed

7 files changed

+162
-73
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
bugfixes:
2+
- region_jcl - Use ZOAU API to write JCL to a data set, so that sequential data sets are allocated properly. The resulting data set can now be submitted from ISPF. (https://github.com/ansible-collections/ibm_zos_cics/pull/170).

plugins/module_utils/_data_set_utils.py

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99

1010
__metaclass__ = type
1111
import re
12-
import tempfile
1312
from ansible_collections.ibm.ibm_zos_core.plugins.module_utils.ansible_module import AnsibleModuleHelper
1413
from ansible_collections.ibm.ibm_zos_cics.plugins.module_utils._response import _execution, MVSExecutionException
1514
from ansible_collections.ibm.ibm_zos_core.plugins.module_utils.zos_mvs_raw import MVSCmd
@@ -290,23 +289,3 @@ def _read_data_set_content(data_set_name):
290289
"RC {0} when reading content from data set {1}".format(
291290
rc, data_set_name), executions)
292291
return executions, stdout
293-
294-
295-
def _write_jcl_to_data_set(jcl, data_set_name):
296-
"""Writes generated JCL content to the specified data set
297-
"""
298-
executions = []
299-
300-
temp = tempfile.NamedTemporaryFile(delete=True)
301-
with open(temp.name, "w") as f:
302-
f.write(jcl)
303-
rc, stdout, stderr = _execute_command("cp -O u {0} \"//'{1}'\"".format(temp.name, data_set_name))
304-
executions.append(
305-
_execution(
306-
name="Copy JCL contents to data set",
307-
rc=rc,
308-
stdout=stdout,
309-
stderr=stderr))
310-
if rc != 0:
311-
raise MVSExecutionException("Failed to copy JCL content to data set", executions)
312-
return executions

plugins/module_utils/_response.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@ def _execution(name, rc, stdout, stderr): # type: (str, int, str, str) -> dict
1919

2020

2121
class MVSExecutionException(Exception):
22-
def __init__(self, message, executions): # type: (str, list[_execution]) -> None
22+
def __init__(self, message, executions): # type: (str, list[dict]) -> None
2323
self.message = message
2424
self.executions = executions

plugins/modules/region_jcl.py

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,8 @@
185185

186186
import string
187187
import math
188+
import traceback
189+
188190
from ansible_collections.ibm.ibm_zos_core.plugins.module_utils.data_set import is_member
189191
from ansible_collections.ibm.ibm_zos_cics.plugins.module_utils._data_set import (
190192
MEGABYTES,
@@ -196,14 +198,32 @@
196198
WARM,
197199
DataSet
198200
)
199-
from ansible_collections.ibm.ibm_zos_cics.plugins.module_utils._data_set_utils import _read_data_set_content, _write_jcl_to_data_set
201+
from ansible_collections.ibm.ibm_zos_cics.plugins.module_utils._data_set_utils import _read_data_set_content
200202
from ansible_collections.ibm.ibm_zos_core.plugins.module_utils.dd_statement import DatasetDefinition
201203
from ansible_collections.ibm.ibm_zos_cics.plugins.module_utils._jcl_helper import (
202204
JCLHelper, DLM, DD_INSTREAM, CONTENT, END_INSTREAM, JOB_CARD, EXECS, JOB_NAME, DDS, NAME
203205
)
204-
from ansible_collections.ibm.ibm_zos_cics.plugins.module_utils._response import MVSExecutionException
206+
from ansible_collections.ibm.ibm_zos_cics.plugins.module_utils._response import (
207+
MVSExecutionException,
208+
_execution
209+
)
205210
from ansible_collections.ibm.ibm_zos_cics.plugins.module_utils._data_set_utils import _run_listds
206211

212+
from ansible_collections.ibm.ibm_zos_core.plugins.module_utils.import_handler import (
213+
ZOAUImportError
214+
)
215+
216+
try:
217+
from zoautil_py import datasets, exceptions
218+
except Exception:
219+
# Use ibm_zos_core's approach to handling zoautil_py imports so sanity tests pass
220+
datasets = ZOAUImportError(traceback.format_exc())
221+
exceptions = ZOAUImportError(traceback.format_exc())
222+
223+
from ansible_collections.ibm.ibm_zos_cics.plugins.module_utils._response import (
224+
MVSExecutionException,
225+
)
226+
207227

208228
DFHSTART = "dfhstart"
209229
SPACE_PRIMARY_DEFAULT = 5
@@ -354,7 +374,7 @@ def create_data_set(self): # type: () -> None
354374
primary_unit=self.primary_unit,
355375
secondary_unit=self.secondary_unit,
356376
volumes=self.volumes,
357-
block_size=4096,
377+
block_size=32720,
358378
record_length=80,
359379
record_format="FB",
360380
disposition="NEW",
@@ -373,7 +393,7 @@ def generate_jcl(self):
373393

374394
def write_jcl(self):
375395
try:
376-
jcl_writer_execution = _write_jcl_to_data_set(self.jcl, self.name)
396+
jcl_writer_execution = self._write_jcl_to_data_set(self.jcl, self.name)
377397
self.executions.extend(jcl_writer_execution)
378398
self.changed = True
379399
except MVSExecutionException as e:
@@ -671,6 +691,35 @@ def _find_sit_parm_key(input_string):
671691
else:
672692
return None
673693

694+
@staticmethod
695+
def _write_jcl_to_data_set(jcl, data_set_name):
696+
"""Writes generated JCL content to the specified data set
697+
"""
698+
executions = []
699+
700+
try:
701+
rc = datasets.write(data_set_name, jcl)
702+
# If rc != 0, ZOAU raises an exception
703+
executions.append(
704+
_execution(
705+
name="Copy JCL contents to data set",
706+
rc=rc,
707+
stdout="",
708+
stderr=""
709+
)
710+
)
711+
except exceptions.DatasetWriteException as e:
712+
raise MVSExecutionException("Failed to copy JCL content to data set", [
713+
_execution(
714+
name="Copy JCL contents to data set",
715+
rc=e.response.rc,
716+
stdout=e.response.stdout_response,
717+
stderr=e.response.stderr_response
718+
)
719+
])
720+
721+
return executions
722+
674723
@staticmethod
675724
def init_argument_spec(): # type: () -> dict
676725
return {
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# (c) Copyright IBM Corp. 2024
2+
# Apache License, Version 2.0 (see https://opensource.org/licenses/Apache-2.0)
3+
---
4+
- name: Test lifecycle of sequential data set for region_jcl module
5+
6+
hosts: "all"
7+
gather_facts: false
8+
environment: "{{ environment_vars }}"
9+
vars:
10+
base_data_set_name: SEQJCL
11+
data_set_path: "{{ region_data_set_path }}.{{ base_data_set_name }}"
12+
13+
module_defaults:
14+
ibm.ibm_zos_cics.region_jcl:
15+
region_data_sets:
16+
template: "{{ region_data_set_path }}.<< data_set_name >>"
17+
dfhstart:
18+
dsn: "{{ data_set_path }}"
19+
cics_data_sets:
20+
template: "{{ cics_install_path }}.<< lib_name >>"
21+
sdfhlic: "{{ cics_install_path }}.LIC.SDFHLIC"
22+
le_data_sets:
23+
template: "{{ le_path }}.<< lib_name >>"
24+
applid: "{{ start_region_applid }}"
25+
26+
tasks:
27+
# #############################################################################
28+
# ############################## Initial Cleanup ##############################
29+
# #############################################################################
30+
31+
- name: Delete base data set {{ base_data_set_path }}
32+
ibm.ibm_zos_core.zos_data_set:
33+
name: "{{ base_data_set_path }}"
34+
state: absent
35+
register: result
36+
retries: 3
37+
until: result is not failed
38+
39+
# #############################################################################
40+
# ############################## Module Testing ###############################
41+
# #############################################################################
42+
43+
- name: Wrap test in block so cleanup always runs
44+
block:
45+
- name: Run region_jcl module with initial state for exisitng PDS
46+
ibm.ibm_zos_cics.region_jcl:
47+
state: initial
48+
register: result
49+
50+
- name: Debug
51+
ansible.builtin.debug:
52+
msg: "{{ result }}"
53+
54+
- name: Assert {{ data_set_path }} created
55+
ansible.builtin.assert:
56+
that:
57+
- result.failed == false
58+
- result.changed == true
59+
- result.start_state.exists == false
60+
- result.end_state.exists == true
61+
- result.msg == ""
62+
63+
# #############################################################################
64+
# ################################## Cleanup ##################################
65+
# #############################################################################
66+
67+
always:
68+
- name: Delete base data set {{ base_data_set_path }}
69+
ibm.ibm_zos_core.zos_data_set:
70+
name: "{{ base_data_set_path }}"
71+
state: absent
72+
register: result
73+
retries: 3
74+
until: result is not failed

tests/unit/module_utils/test_dataset_utils.py

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -657,46 +657,3 @@ def test__read_data_set_content_bad_rc():
657657
assert e.executions == expected_executions
658658
else:
659659
assert False
660-
661-
662-
def test__write_jcl_to_data_set():
663-
data_set_name = "TEST.DATA.SET"
664-
jcl = ""
665-
666-
rc = 0
667-
stdout = ""
668-
stderr = ""
669-
data_set_utils._execute_command = MagicMock(return_value=(rc, stdout, stderr))
670-
671-
expected_executions = [{
672-
"name": "Copy JCL contents to data set",
673-
"rc": 0,
674-
"stdout": "",
675-
"stderr": ""
676-
}]
677-
678-
executions = data_set_utils._write_jcl_to_data_set(jcl, data_set_name)
679-
680-
assert expected_executions == executions
681-
682-
683-
def test__write_jcl_to_data_set_fail():
684-
data_set_name = "TEST.DATA.SET"
685-
jcl = ""
686-
rc = 99
687-
stdout = "cp failed"
688-
stderr = "stderr"
689-
data_set_utils._execute_command = MagicMock(return_value=(rc, stdout, stderr))
690-
691-
expected_executions = [{
692-
"name": "Copy JCL contents to data set",
693-
"rc": 99,
694-
"stdout": "cp failed",
695-
"stderr": "stderr"
696-
}]
697-
698-
with pytest.raises(MVSExecutionException) as e:
699-
data_set_utils._write_jcl_to_data_set(jcl, data_set_name)
700-
701-
assert e.value.message == "Failed to copy JCL content to data set"
702-
assert e.value.executions == expected_executions

tests/unit/modules/test_region_jcl.py

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,7 +1072,14 @@ def test_initial_state():
10721072
)
10731073
)
10741074

1075-
_data_set_utils._execute_command = MagicMock(return_value=(0, "", ""))
1075+
region_jcl_module._write_jcl_to_data_set = MagicMock(return_value=[
1076+
_execution(
1077+
name="Copy JCL contents to data set",
1078+
rc=0,
1079+
stdout="",
1080+
stderr=""
1081+
)
1082+
])
10761083

10771084
region_jcl_module.main()
10781085
expected_result = dict(
@@ -1138,7 +1145,14 @@ def test_initial_state_pre_existing():
11381145
)
11391146
)
11401147

1141-
_data_set_utils._execute_command = MagicMock(return_value=(0, "", ""))
1148+
region_jcl_module._write_jcl_to_data_set = MagicMock(return_value=[
1149+
_execution(
1150+
name="Copy JCL contents to data set",
1151+
rc=0,
1152+
stdout="",
1153+
stderr=""
1154+
)
1155+
])
11421156

11431157
region_jcl_module.main()
11441158
expected_result = dict(
@@ -1430,7 +1444,14 @@ def test_initial_state_member():
14301444
]
14311445
)
14321446

1433-
_data_set_utils._execute_command = MagicMock(return_value=(0, "", ""))
1447+
region_jcl_module._write_jcl_to_data_set = MagicMock(return_value=[
1448+
_execution(
1449+
name="Copy JCL contents to data set",
1450+
rc=0,
1451+
stdout="",
1452+
stderr=""
1453+
)
1454+
])
14341455

14351456
region_jcl_module.main()
14361457
expected_result = dict(
@@ -1550,7 +1571,14 @@ def test_initial_state_pre_existing_member():
15501571
return_value=MVSCmdResponse(0, IDCAMS_delete(MEMBER_DS_NAME), "")
15511572
)
15521573

1553-
_data_set_utils._execute_command = MagicMock(return_value=(0, "", ""))
1574+
region_jcl_module._write_jcl_to_data_set = MagicMock(return_value=[
1575+
_execution(
1576+
name="Copy JCL contents to data set",
1577+
rc=0,
1578+
stdout="",
1579+
stderr=""
1580+
)
1581+
])
15541582

15551583
region_jcl_module.main()
15561584
expected_result = dict(

0 commit comments

Comments
 (0)