6
6
# FOR INTERNAL USE IN THE COLLECTION ONLY.
7
7
8
8
from __future__ import (absolute_import , division , print_function )
9
- from os import remove
10
9
__metaclass__ = type
11
10
12
11
12
+ from ansible_collections .ibm .ibm_zos_core .plugins .module_utils .zos_mvs_raw import MVSCmd , MVSCmdResponse
13
+ from ansible_collections .ibm .ibm_zos_core .plugins .module_utils .dd_statement import StdoutDefinition , DatasetDefinition , DDStatement , InputDefinition
13
14
from ansible_collections .ibm .ibm_zos_cics .plugins .module_utils ._response import MVSExecutionException , _execution
14
- from ansible_collections .ibm .ibm_zos_cics .plugins .module_utils ._data_set_utils import MVS_CMD_RETRY_ATTEMPTS , _submit_jcl , _get_job_output
15
- from tempfile import NamedTemporaryFile
16
- from time import sleep
15
+ from ansible_collections .ibm .ibm_zos_cics .plugins .module_utils ._data_set_utils import MVS_CMD_RETRY_ATTEMPTS
17
16
18
17
19
18
def _get_value_from_line (line ): # type: (list[str]) -> str | None
@@ -27,6 +26,18 @@ def _get_filtered_list(elements, target): # type: (list[str],str) -> list[str]
27
26
return list (filter (lambda x : target in x , elements ))
28
27
29
28
29
+ def _get_rmutl_dds (
30
+ location ,
31
+ sdfhload ,
32
+ cmd ): # type: (str, str, str) -> list[DDStatement]
33
+ return [
34
+ DDStatement ('steplib' , DatasetDefinition (sdfhload )),
35
+ DDStatement ('dfhgcd' , DatasetDefinition (location )),
36
+ DDStatement ('sysin' , InputDefinition (content = cmd )),
37
+ DDStatement ('sysprint' , StdoutDefinition ()),
38
+ ]
39
+
40
+
30
41
def _get_reason_code (stdout_lines_arr ): # type: (list[str]) -> str | None
31
42
if len (stdout_lines_arr ) == 0 :
32
43
return None
@@ -57,132 +68,63 @@ def _get_catalog_records(stdout): # type: (str) -> tuple[str | None, str | None
57
68
return (autostart_override , nextstart )
58
69
59
70
60
- def _create_dfhrmutl_jcl (location , sdfhload , cmd = "" ):
61
- steplib_line = f"//STEPLIB DD DSNAME={ sdfhload } ,DISP=SHR"
62
- dfhgcd_line = f"//DFHGCD DD DSNAME={ location } ,DISP=OLD"
63
- # Validate line lengths
64
- _validate_line_length (steplib_line , sdfhload , "cics_data_sets" )
65
- _validate_line_length (dfhgcd_line , location , "region_data_sets" )
66
-
67
- jcl = ""
68
- if (cmd == "" ):
69
- jcl = f'''
70
- //DFHRMUTL JOB
71
- //RMUTL EXEC PGM=DFHRMUTL,REGION=1M
72
- { steplib_line }
73
- //SYSPRINT DD SYSOUT=*
74
- { dfhgcd_line }
75
- //SYSIN DD *
76
- /*
77
- '''
78
- else :
79
- jcl = f'''
80
- //DFHRMUTL JOB
81
- //RMUTL EXEC PGM=DFHRMUTL,REGION=1M
82
- { steplib_line }
83
- //SYSPRINT DD SYSOUT=*
84
- { dfhgcd_line }
85
- //SYSIN DD *
86
- { cmd }
87
- /*
88
- '''
89
-
90
- # Create a temporary file
91
- with NamedTemporaryFile (mode = 'w+' , delete = False ) as fp :
92
- fp .write (jcl )
93
- fp .flush ()
94
-
95
- # Get the temporary file's name
96
- qualified_file_path = fp .name
97
- return qualified_file_path
98
-
99
-
100
- def _validate_line_length (line , name , parameter ):
101
- """
102
- Validates that the JCL line does not exceed MAX_LINE_LENGTH.
103
- Raises ValueError if validation fails.
104
- """
105
- if len (line ) > MAX_LINE_LENGTH :
106
- raise ValueError (f"Value supplied for { parameter } ({ name } ) is { len (line ) - MAX_LINE_LENGTH } characters too long" )
107
- return True
108
-
109
-
110
71
def _run_dfhrmutl (
111
72
location , # type: str
112
73
sdfhload , # type: str
113
74
cmd = "" # type: str
114
75
):
115
76
# type: (...) -> tuple[list[dict[str, str| int]], tuple[str | None, str | None]] | list[dict[str, str| int]]
116
-
117
- qualified_file_path = _create_dfhrmutl_jcl (
118
- location ,
119
- sdfhload ,
120
- cmd
121
- )
122
77
executions = []
123
- try :
124
- for x in range (MVS_CMD_RETRY_ATTEMPTS ):
125
- dfhrmutl_response , jcl_executions = _execute_dfhrmutl (qualified_file_path )
126
- dfhrmutl_rc = dfhrmutl_response ["ret_code" ].get ("code" )
127
-
128
- allContent = []
129
- for ddname in dfhrmutl_response .get ("ddnames" ):
130
- allContent += ddname .get ("content" )
131
- stdout_raw = "" .join (allContent )
132
-
133
- executions .append (jcl_executions )
134
- executions .append (
135
- _execution (
136
- name = "DFHRMUTL - {0} - Run {1}" .format (
137
- "Get current catalog" if cmd == "" else "Updating autostart override" ,
138
- x + 1 ),
139
- rc = dfhrmutl_rc ,
140
- stdout = stdout_raw ,
141
- stderr = dfhrmutl_response ["ret_code" ].get ("msg_txt" , "" )))
142
-
143
- if dfhrmutl_rc not in (0 , 16 ):
144
- raise MVSExecutionException (
145
- "DFHRMUTL failed with RC {0}" .format (
146
- dfhrmutl_rc ), executions )
147
-
148
- if dfhrmutl_rc == 0 :
149
- break
150
- if dfhrmutl_rc == 16 :
78
+
79
+ for x in range (MVS_CMD_RETRY_ATTEMPTS ):
80
+ dfhrmutl_response = _execute_dfhrmutl (location , sdfhload , cmd )
81
+ execution_entry = _execution (
82
+ name = "DFHRMUTL - {0} - Run {1}" .format (
83
+ "Get current catalog" if cmd == "" else "Updating autostart override" ,
84
+ x + 1 ),
85
+ rc = dfhrmutl_response .rc ,
86
+ stdout = dfhrmutl_response .stdout ,
87
+ stderr = dfhrmutl_response .stderr
88
+ )
89
+ executions .append (execution_entry )
90
+
91
+ if dfhrmutl_response .rc == 0 :
92
+ break
93
+ if dfhrmutl_response .rc != 0 :
94
+ # DFHRMUTL fails when running with MVSCMD so check it ran successfully
95
+ if DFHRMUTL_PROGRAM_HEADER not in dfhrmutl_response .stdout or SUBPROCESS_EXIT_MESSAGE not in dfhrmutl_response .stderr :
151
96
formatted_stdout_lines = [
152
97
"{0}" .format (element .replace (" " , "" ).upper ())
153
- for element in stdout_raw .split ("\n " )
98
+ for element in dfhrmutl_response . stdout .split ("\n " )
154
99
]
155
100
stdout_with_rc = list (filter (lambda x : "REASON:X" in x , formatted_stdout_lines ))
156
101
157
102
reason_code = _get_reason_code (stdout_with_rc )
158
103
if reason_code and reason_code != "A8" :
159
104
raise MVSExecutionException (
160
- "DFHRMUTL failed with RC 16 - {0}" . format ( stdout_with_rc [0 ]) , executions
105
+ f "DFHRMUTL failed with RC { dfhrmutl_response . rc } - { stdout_with_rc [0 ]} " , executions
161
106
)
162
107
elif reason_code is None :
163
108
raise MVSExecutionException (
164
- "DFHRMUTL failed with RC 16 but no reason code was found" ,
165
- executions ,
109
+ f "DFHRMUTL failed with RC { dfhrmutl_response . rc } but no reason code was found" ,
110
+ executions
166
111
)
112
+ else :
113
+ execution_entry ["rc" ] = 0
114
+ break
167
115
168
- if cmd != "" :
169
- return executions
170
- finally :
171
- remove (qualified_file_path )
172
- return executions , _get_catalog_records (stdout_raw )
173
-
174
-
175
- def _execute_dfhrmutl (rmutl_jcl_path , job_name = "DFHRMUTL" ):
176
- executions = _submit_jcl (rmutl_jcl_path , job_name )
177
- job_id = executions [0 ].get ("stdout" ).strip ()
116
+ if cmd != "" :
117
+ return executions
178
118
179
- # Give RMUTL a second to run
180
- sleep (JOB_SUBMIT_WAIT_TIME )
119
+ return executions , _get_catalog_records (dfhrmutl_response .stdout )
181
120
182
- job , job_executions = _get_job_output (job_id , job_name )
183
- executions .append (job_executions )
184
121
185
- return job , executions
122
+ def _execute_dfhrmutl (location , sdfhload , cmd = "" ): # type: (str, str, str) -> MVSCmdResponse
123
+ return MVSCmd .execute (
124
+ pgm = "DFHRMUTL" ,
125
+ dds = _get_rmutl_dds (location = location , sdfhload = sdfhload , cmd = cmd ),
126
+ verbose = True ,
127
+ debug = False )
186
128
187
129
188
130
def _get_idcams_cmd_gcd (dataset ): # type: (dict) -> dict
@@ -206,7 +148,6 @@ def _get_idcams_cmd_gcd(dataset): # type: (dict) -> dict
206
148
return defaults
207
149
208
150
209
- # IDCAMS Consts
210
151
RECORD_COUNT_DEFAULT = 4089
211
152
RECORD_SIZE_DEFAULT = 32760
212
153
CONTROL_INTERVAL_SIZE_DEFAULT = 32768
@@ -216,6 +157,5 @@ def _get_idcams_cmd_gcd(dataset): # type: (dict) -> dict
216
157
CA_PERCENT = 10
217
158
SHARE_CROSSREGION = 2
218
159
219
- # RMUTL Consts
220
- MAX_LINE_LENGTH = 72
221
- JOB_SUBMIT_WAIT_TIME = 1
160
+ DFHRMUTL_PROGRAM_HEADER = "===DFHRMUTL CICS RECOVERY MANAGER BATCH UTILITY==="
161
+ SUBPROCESS_EXIT_MESSAGE = "Attach Exit code: 0 from DFHRMUTL."
0 commit comments