Skip to content

Commit 40c93eb

Browse files
Zoau13 updates (#163)
* Test getting rmutl sysout from a temp dataset Signed-off-by: Stewart Francis <[email protected]> * Some more ZOAU 1.3 compatibility updates in tests Signed-off-by: Stewart Francis <[email protected]> * Use a real data set for sysprint for rmutl so we can get the output Signed-off-by: Stewart Francis <[email protected]> * Reinstate unit tests Signed-off-by: Stewart Francis <[email protected]> * Use ibm_zos_core's approach to handling import errors so sanity tests pass Signed-off-by: Stewart Francis <[email protected]> * Fix sanity tests Signed-off-by: Stewart Francis <[email protected]> * Remove unused cics_version utility, as it now experiences https://www.ibm.com/support/pages/apar/PH64461 with ZOAU 1.3 Signed-off-by: Stewart Francis <[email protected]> * test Signed-off-by: Stewart Francis <[email protected]> * test Signed-off-by: Stewart Francis <[email protected]> * test Signed-off-by: Stewart Francis <[email protected]> * Try a different approach to getting job name from id Signed-off-by: Stewart Francis <[email protected]> * Reintroduce check for at least one of job name and job id Signed-off-by: Stewart Francis <[email protected]> * Bespoke checking for running jobs by ID, handle ugly ZOAU exception Signed-off-by: Stewart Francis <[email protected]> * Fix pep8 issues Signed-off-by: Stewart Francis <[email protected]> * Fix pylint issues Signed-off-by: Stewart Francis <[email protected]> * Move action integration tests to be module unit tests Signed-off-by: Stewart Francis <[email protected]> * Fix sanity test errors, add changelog fragment Signed-off-by: Stewart Francis <[email protected]> * fix yaml lint... Signed-off-by: Stewart Francis <[email protected]> * Revert rename of action module Signed-off-by: Stewart Francis <[email protected]> * Fix a bug where job name and id got swapped Signed-off-by: Stewart Francis <[email protected]> * Fix pep8 sanity test Signed-off-by: Stewart Francis <[email protected]> * Address review comments, use destructuring Signed-off-by: Stewart Francis <[email protected]> * define messages once only Signed-off-by: Stewart Francis <[email protected]> * fail if we can't find the job name Signed-off-by: Stewart Francis <[email protected]> * test Signed-off-by: Stewart Francis <[email protected]> * test Signed-off-by: Stewart Francis <[email protected]> * Fix a syntax error Signed-off-by: Stewart Francis <[email protected]> * Don't fail if there's no job to stop Signed-off-by: Stewart Francis <[email protected]> * Fix lint issue Signed-off-by: Stewart Francis <[email protected]> --------- Signed-off-by: Stewart Francis <[email protected]>
1 parent 53a7f77 commit 40c93eb

File tree

21 files changed

+397
-878
lines changed

21 files changed

+397
-878
lines changed

.devcontainer/setup.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ fi
1111

1212
python3 -m pip install --user ansible-core==2.16
1313

14-
ansible-galaxy collection install ibm.ibm_zos_core:==1.9.1 -p /workspace/collections
14+
ansible-galaxy collection install ibm.ibm_zos_core:==1.10.0 -p /workspace/collections
1515
ansible-galaxy collection install community.general -p /workspace/collections
1616

1717
echo -e "[defaults]\nstdout_callback=community.general.yaml\nCOLLECTIONS_PATHS=/workspace/collections" > ~/.ansible.cfg

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ docs/build/
66
__pycache__
77
venv*
88
.venv
9+
.ansible
910
core
1011
tests/output
1112
ibm-ibm_zos_cics-*.tar.gz
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
major_changes:
2+
- Update compatibility of the collection to Z Open Automation Utilities 1.3.x and ibm_zos_core 1.10+ (https://github.com/ansible-collections/ibm_zos_cics/pull/163).

plugins/action/stop_region.py

Lines changed: 65 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,17 @@
4747
class ActionModule(ActionBase):
4848
def run(self, tmp=None, task_vars=None):
4949
self._setup(tmp, task_vars)
50-
self._execute_stop_module()
5150

52-
if self.failed:
53-
return self.get_result()
51+
self.job_name, self.job_id, self.stop_mode, self.sdtran, self.no_sdtran, self.timeout = validate_module_params(
52+
self.module_args.get(JOB_NAME),
53+
self.module_args.get(JOB_ID),
54+
self.module_args.get(MODE),
55+
self.module_args.get(SDTRAN),
56+
self.module_args.get(NO_SDTRAN),
57+
self.module_args.get(TIMEOUT, TIMEOUT_DEFAULT)
58+
)
5459

55-
self._parse_module_params()
60+
self.job_status = EXECUTING
5661

5762
try:
5863
self._get_job_data()
@@ -115,52 +120,17 @@ def get_result(self, msg=None):
115120
EXECUTIONS: self.executions,
116121
}
117122

118-
def _execute_stop_module(self):
119-
stop_module_output = self._execute_module(
120-
module_name=STOP_MODULE_NAME,
121-
module_args=self.module_args,
122-
task_vars=self.task_vars,
123-
tmp=self.tmp,
124-
)
125-
self.failed = stop_module_output.get(FAILED, self.failed)
126-
self.msg = stop_module_output.get(MSG, self.msg)
127-
128-
def _parse_module_params(self):
129-
self.job_name = self.module_args.get(JOB_NAME)
130-
self.job_id = self.module_args.get(JOB_ID)
131-
self.stop_mode = self.module_args.get(MODE)
132-
self.sdtran = self.module_args.get(SDTRAN)
133-
self.no_sdtran = self.module_args.get(NO_SDTRAN)
134-
self.timeout = self.module_args.get(TIMEOUT, TIMEOUT_DEFAULT)
135-
self.job_status = EXECUTING
136-
137123
def _get_job_data(self):
138124
if self.job_id and self.job_name:
139-
self.job_status = self._get_job_status()
125+
self.job_status = self._get_job_status_by_name_and_id()
140126
elif self.job_name:
141-
running_jobs = self._get_running_jobs()
142-
143-
if len(running_jobs) == 0:
144-
self.job_status = "MISSING"
145-
return
146-
if len(running_jobs) > 1:
147-
self.job_status = "MULTIPLE"
148-
raise AnsibleActionFail(
149-
"Cannot ambiguate between multiple running jobs with the same name ({0}). Use `job_id` as a parameter to specify the correct job.".format(
150-
self.job_name))
151-
152-
self.job_id = running_jobs[0][JOB_ID]
153-
self.job_status = running_jobs[0][STATUS]
154-
127+
self.job_id, self.job_status = self._get_job_id_and_status_by_name()
155128
elif self.job_id:
156-
self.job_name = self._get_job_name_from_id()
157-
self.job_status = self._get_job_status()
158-
159-
def _get_job_name_from_id(self):
160-
job_query_response = self.execute_zos_job_query(self.job_id)
161-
return _get_job_name_from_query(job_query_response, self.job_id)
129+
self.job_name, self.job_status = self._get_job_name_and_status_by_id()
130+
else:
131+
raise Exception("Neither job_name nor job_id was set. This shouldn't happen according to the argument spec")
162132

163-
def _get_job_status(self):
133+
def _get_job_status_by_name_and_id(self): # type: () -> str
164134
tso_status_response = self.execute_zos_tso_cmd(
165135
TSO_STATUS_ID_COMMAND.format(self.job_name, self.job_id)
166136
)
@@ -173,8 +143,45 @@ def _get_job_status(self):
173143
if job_status == "COMBINATION INVALID":
174144
raise AnsibleActionFail(
175145
"No jobs found with name {0} and ID {1}".format(self.job_name, self.job_id))
146+
176147
return job_status
177148

149+
def _get_job_id_and_status_by_name(self): # type: () -> (str, str)
150+
# If we have a name but no ID, we use a TSO command to get the job ID
151+
running_jobs = self._get_running_jobs()
152+
153+
if len(running_jobs) == 0:
154+
# In the event that the job ID is missing, we're still not exposed to the ZOAU 'bug' as we have not used zos_job_query
155+
return (None, "MISSING")
156+
157+
if len(running_jobs) > 1:
158+
raise AnsibleActionFail(
159+
"Cannot disambiguate between multiple running jobs with the same name ({0}). Use `job_id` as a parameter to specify the correct job.".format(
160+
self.job_name))
161+
162+
return (running_jobs[0][JOB_ID], running_jobs[0][STATUS])
163+
164+
def _get_job_name_and_status_by_id(self): # type: () -> (str, str)
165+
# We're going to execute this via the stop_region module, to massage the response format into something sensible
166+
stop_module_output = self._execute_module(
167+
module_name=STOP_MODULE_NAME,
168+
module_args={JOB_ID: self.job_id},
169+
task_vars=self.task_vars,
170+
)
171+
172+
self.executions.append({
173+
NAME: "Get job name and status for job ID {0}".format(self.job_id),
174+
RC: 1 if stop_module_output.get("failed") else 0,
175+
RETURN: stop_module_output,
176+
})
177+
178+
if stop_module_output.get("failed"):
179+
raise AnsibleActionFail(
180+
message=stop_module_output.get("msg", "Failure getting job name and status from ID")
181+
)
182+
183+
return (stop_module_output["job_name"], stop_module_output["job_status"])
184+
178185
def _get_running_jobs(self):
179186
tso_query_response = self.execute_zos_tso_cmd(
180187
TSO_STATUS_COMMAND.format(self.job_name)
@@ -233,19 +240,6 @@ def execute_zos_tso_cmd(self, command):
233240
task_vars=self.task_vars,
234241
)
235242

236-
def execute_zos_job_query(self, job_id):
237-
query_response = self._execute_module(
238-
module_name="ibm.ibm_zos_core.zos_job_query",
239-
module_args={JOB_ID: job_id},
240-
task_vars=self.task_vars,
241-
)
242-
self.executions.append({
243-
NAME: "ZOS Job Query - {0}".format(job_id),
244-
RC: 0 if query_response.get("message", "-") == "" and isinstance(query_response.get("jobs", {}), list) else 1,
245-
RETURN: query_response
246-
})
247-
return query_response
248-
249243
def execute_zos_operator_cmd(self, command):
250244
operator_response = self._execute_module(
251245
module_name="ibm.ibm_zos_core.zos_operator",
@@ -284,6 +278,19 @@ def execute_cancel_shell_cmd(self, job_name, job_id):
284278
return cancel_response
285279

286280

281+
def validate_module_params(job_name, job_id, stop_mode, sdtran, no_sdtran, timeout):
282+
if not job_id and not job_name:
283+
raise AnsibleActionFail("At least one of {0} or {1} must be specified".format(
284+
JOB_ID, JOB_NAME))
285+
286+
if sdtran and len(sdtran) > 4:
287+
raise AnsibleActionFail(
288+
"Value: {0}, is invalid. SDTRAN value must be 1-4 characters.".format(sdtran)
289+
)
290+
291+
return (job_name, job_id, stop_mode, sdtran, no_sdtran, timeout)
292+
293+
287294
def get_datetime_now():
288295
return datetime.now()
289296

@@ -351,29 +358,6 @@ def _get_job_info_from_status(tso_query_response, job_name):
351358
return jobs
352359

353360

354-
def _get_job_name_from_query(job_query_response, job_id):
355-
if job_query_response.get(FAILED):
356-
raise AnsibleActionFail(
357-
"Job query failed - {0}".format(
358-
job_query_response.get("message", "(No failure message provided by zos_job_query)")))
359-
360-
jobs = job_query_response.get("jobs", [])
361-
362-
if len(jobs) == 0:
363-
raise AnsibleActionFail("No jobs found with id {0}".format(job_id))
364-
elif (
365-
len(jobs) == 1
366-
and jobs[0].get(JOB_NAME, "") == "*"
367-
and jobs[0].get("ret_code", {}).get("msg", "") == "JOB NOT FOUND"
368-
):
369-
raise AnsibleActionFail("No jobs found with id {0}".format(job_id))
370-
elif len(jobs) > 1:
371-
raise AnsibleActionFail(
372-
"Multiple jobs found with ID {0}".format(job_id))
373-
374-
return jobs[0].get(JOB_NAME)
375-
376-
377361
def _get_job_status_name_id(tso_status_command_response, job_name, job_id):
378362
if len(tso_status_command_response.get("output", [])) != 1:
379363
raise AnsibleActionFail("Output not received for TSO STATUS command")

plugins/module_utils/_cicsgetversion.py

Lines changed: 0 additions & 29 deletions
This file was deleted.

plugins/module_utils/_global_catalog.py

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,24 @@
88
from __future__ import (absolute_import, division, print_function)
99
__metaclass__ = type
1010

11+
import traceback
1112

1213
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
14+
from ansible_collections.ibm.ibm_zos_core.plugins.module_utils.dd_statement import OutputDefinition, DatasetDefinition, DDStatement, InputDefinition
1415
from ansible_collections.ibm.ibm_zos_cics.plugins.module_utils._response import MVSExecutionException, _execution
1516
from ansible_collections.ibm.ibm_zos_cics.plugins.module_utils._data_set_utils import MVS_CMD_RETRY_ATTEMPTS
1617

18+
from ansible_collections.ibm.ibm_zos_core.plugins.module_utils.import_handler import (
19+
ZOAUImportError
20+
)
21+
22+
try:
23+
from zoautil_py import datasets, exceptions
24+
except Exception:
25+
# Use ibm_zos_core's approach to handling zoautil_py imports so sanity tests pass
26+
datasets = ZOAUImportError(traceback.format_exc())
27+
exceptions = ZOAUImportError(traceback.format_exc())
28+
1729

1830
def _get_value_from_line(line): # type: (list[str]) -> str | None
1931
val = None
@@ -26,18 +38,6 @@ def _get_filtered_list(elements, target): # type: (list[str],str) -> list[str]
2638
return list(filter(lambda x: target in x, elements))
2739

2840

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-
4141
def _get_reason_code(stdout_lines_arr): # type: (list[str]) -> str | None
4242
if len(stdout_lines_arr) == 0:
4343
return None
@@ -120,12 +120,34 @@ def _run_dfhrmutl(
120120

121121

122122
def _execute_dfhrmutl(location, sdfhload, cmd=""): # type: (str, str, str) -> MVSCmdResponse
123-
return MVSCmd.execute(
123+
sysprint = OutputDefinition(record_length=133)
124+
125+
dds = [
126+
DDStatement('steplib', DatasetDefinition(sdfhload)),
127+
DDStatement('dfhgcd', DatasetDefinition(location)),
128+
DDStatement('sysin', InputDefinition(content=cmd)),
129+
DDStatement('sysprint', sysprint)
130+
]
131+
132+
response = MVSCmd.execute(
124133
pgm="DFHRMUTL",
125-
dds=_get_rmutl_dds(location=location, sdfhload=sdfhload, cmd=cmd),
134+
dds=dds,
126135
verbose=True,
127136
debug=False)
128137

138+
try:
139+
response.stdout = datasets.read(sysprint.name)
140+
datasets.delete(sysprint.name)
141+
except exceptions.ZOAUException as e:
142+
raise MVSExecutionException(
143+
msg="Unable to read SYSPRINT dataset {0}".format(sysprint.name),
144+
rc=e.response.rc,
145+
stdout=e.response.stdout_response,
146+
stderr=e.response.stderr_response
147+
)
148+
149+
return response
150+
129151

130152
def _get_idcams_cmd_gcd(dataset): # type: (dict) -> dict
131153
defaults = {

0 commit comments

Comments
 (0)