diff --git a/changelogs/fragments/2207-SYSIN-support-zos_job_output.yml b/changelogs/fragments/2207-SYSIN-support-zos_job_output.yml new file mode 100644 index 000000000..230b112ea --- /dev/null +++ b/changelogs/fragments/2207-SYSIN-support-zos_job_output.yml @@ -0,0 +1,3 @@ +minor_changes: + - zos_job_output - Adds support to query SYSIN DDs from a job with new option input. + (https://github.com/ansible-collections/ibm_zos_core/pull/2207) \ No newline at end of file diff --git a/changelogs/fragments/2213-test-case-conditional-failure-2-19.yml b/changelogs/fragments/2213-test-case-conditional-failure-2-19.yml new file mode 100644 index 000000000..72bf5c59a --- /dev/null +++ b/changelogs/fragments/2213-test-case-conditional-failure-2-19.yml @@ -0,0 +1,16 @@ +trivial: + - test_zos_copy_func.py - modified test case `test_job_script_async` + to resolve porting issues to ansible 2.19. + (https://github.com/ansible-collections/ibm_zos_core/pull/2213). + + - test_zos_job_submit_func.py - modified test case `test_job_submit_async` + to resolve porting issues to ansible 2.19. + (https://github.com/ansible-collections/ibm_zos_core/pull/2213). + + - test_zos_script_func.py - modified test case `test_job_script_async` + to resolve porting issues to ansible 2.19. + (https://github.com/ansible-collections/ibm_zos_core/pull/2213). + + - test_zos_unarchive_func.py - modified test case `test_zos_unarchive_async` + to resolve porting issues to ansible 2.19. + (https://github.com/ansible-collections/ibm_zos_core/pull/2213). \ No newline at end of file diff --git a/plugins/module_utils/job.py b/plugins/module_utils/job.py index 1b0351d68..464069725 100644 --- a/plugins/module_utils/job.py +++ b/plugins/module_utils/job.py @@ -58,7 +58,7 @@ ]) -def job_output(job_id=None, owner=None, job_name=None, dd_name=None, dd_scan=True, duration=0, timeout=0, start_time=timer()): +def job_output(job_id=None, owner=None, job_name=None, dd_name=None, sysin=False, dd_scan=True, duration=0, timeout=0, start_time=timer()): """Get the output from a z/OS job based on various search criteria. Keyword Parameters @@ -71,6 +71,8 @@ def job_output(job_id=None, owner=None, job_name=None, dd_name=None, dd_scan=Tru The job name search for (default: {None}). dd_name : str The data definition to retrieve (default: {None}). + sysin : bool + The input DD to retrieve SYSIN value (default: {False}). dd_scan : bool Whether or not to pull information from the dd's for this job {default: {True}}. duration : int @@ -112,6 +114,7 @@ def job_output(job_id=None, owner=None, job_name=None, dd_name=None, dd_scan=Tru job_name=job_name, dd_name=dd_name, duration=duration, + sysin=sysin, dd_scan=dd_scan, timeout=timeout, start_time=start_time @@ -128,6 +131,7 @@ def job_output(job_id=None, owner=None, job_name=None, dd_name=None, dd_scan=Tru owner=owner, job_name=job_name, dd_name=dd_name, + sysin=sysin, dd_scan=dd_scan, duration=duration, timeout=timeout, @@ -287,7 +291,7 @@ def _parse_steps(job_str): return stp -def _get_job_status(job_id="*", owner="*", job_name="*", dd_name=None, dd_scan=True, duration=0, timeout=0, start_time=timer()): +def _get_job_status(job_id="*", owner="*", job_name="*", dd_name=None, sysin=False, dd_scan=True, duration=0, timeout=0, start_time=timer()): """Get job status. Parameters @@ -300,6 +304,8 @@ def _get_job_status(job_id="*", owner="*", job_name="*", dd_name=None, dd_scan=T The job name search for (default: {None}). dd_name : str The data definition to retrieve (default: {None}). + sysin : bool + The input DD SYSIN (default: {False}). dd_scan : bool Whether or not to pull information from the dd's for this job {default: {True}}. duration : int @@ -381,7 +387,7 @@ def _get_job_status(job_id="*", owner="*", job_name="*", dd_name=None, dd_scan=T job["queue_position"] = entry.queue_position job["program_name"] = entry.program_name job["class"] = "" - job["steps"] = [] + job["ret_code"]["steps"] = [] job["ddnames"] = [] job["duration"] = duration if hasattr(entry, "execution_time"): @@ -405,7 +411,7 @@ def _get_job_status(job_id="*", owner="*", job_name="*", dd_name=None, dd_scan=T list_of_dds = [] try: - list_of_dds = jobs.list_dds(entry.job_id) + list_of_dds = jobs.list_dds(entry.job_id, sysin=sysin) except exceptions.DDQueryException: is_dd_query_exception = True @@ -424,7 +430,7 @@ def _get_job_status(job_id="*", owner="*", job_name="*", dd_name=None, dd_scan=T try: # Note, in the event of an exception, eg job has TYPRUN=HOLD # list_of_dds will still be populated with valuable content - list_of_dds = jobs.list_dds(entry.job_id) + list_of_dds = jobs.list_dds(entry.job_id, sysin=sysin) is_jesjcl = True if search_dictionaries("dd_name", "JESJCL", list_of_dds) else False is_job_error_status = True if entry.status in JOB_ERROR_STATUSES else False except exceptions.DDQueryException: @@ -493,7 +499,7 @@ def _get_job_status(job_id="*", owner="*", job_name="*", dd_name=None, dd_scan=T dd["content"] = tmpcont.split("\n") - job["steps"].extend(_parse_steps(tmpcont)) + job["ret_code"]["steps"].extend(_parse_steps(tmpcont)) job["ddnames"].append(dd) if len(job["class"]) < 1: diff --git a/plugins/modules/zos_job_output.py b/plugins/modules/zos_job_output.py index 19be24856..00fef50ba 100644 --- a/plugins/modules/zos_job_output.py +++ b/plugins/modules/zos_job_output.py @@ -33,6 +33,7 @@ like "*". - If there is no ddname, or if ddname="?", output of all the ddnames under the given job will be displayed. + - If SYSIN DDs are needed, C(input) should be set to C(true). version_added: "1.0.0" author: - "Jack Ho (@jacklotusho)" @@ -61,6 +62,12 @@ (e.g "JESJCL", "?") type: str required: false + input: + description: + - Whether to include SYSIN DDs as part of the output. + type: bool + default: false + required: false attributes: action: @@ -90,6 +97,11 @@ job_name: "*" owner: "IBMUSER" ddname: "?" + +- name: Query a job's output including SYSIN DDs + zos_job_output: + job_id: "JOB00548" + input: true """ RETURN = r""" @@ -496,6 +508,7 @@ def run_module(): job_name=dict(type="str", required=False), owner=dict(type="str", required=False), ddname=dict(type="str", required=False), + input=dict(type="bool", required=False, default=False), ) module = AnsibleModule(argument_spec=module_args, supports_check_mode=True) @@ -505,6 +518,7 @@ def run_module(): job_name=dict(type="job_identifier", required=False), owner=dict(type="str", required=False), ddname=dict(type="str", required=False), + input=dict(type="bool", required=False, default=False), ) try: @@ -521,13 +535,14 @@ def run_module(): job_name = module.params.get("job_name") owner = module.params.get("owner") ddname = module.params.get("ddname") + sysin = module.params.get("input") if not job_id and not job_name and not owner: module.fail_json(msg="Please provide a job_id or job_name or owner") try: results = {} - results["jobs"] = job_output(job_id=job_id, owner=owner, job_name=job_name, dd_name=ddname) + results["jobs"] = job_output(job_id=job_id, owner=owner, job_name=job_name, dd_name=ddname, sysin=sysin) results["changed"] = False except zoau_exceptions.JobFetchException as fetch_exception: module.fail_json( diff --git a/tests/functional/modules/test_zos_copy_func.py b/tests/functional/modules/test_zos_copy_func.py index a660d7576..6d13aa104 100644 --- a/tests/functional/modules/test_zos_copy_func.py +++ b/tests/functional/modules/test_zos_copy_func.py @@ -257,7 +257,7 @@ async_status: jid: "{{{{ copy_output.ansible_job_id }}}}" register: job_result - until: job_result.finished + until: job_result.finished | bool retries: 10 delay: 30 diff --git a/tests/functional/modules/test_zos_job_output_func.py b/tests/functional/modules/test_zos_job_output_func.py index e1db120d2..2c41f0669 100644 --- a/tests/functional/modules/test_zos_job_output_func.py +++ b/tests/functional/modules/test_zos_job_output_func.py @@ -30,6 +30,19 @@ // """ +JCL_FILE_CONTENTS_SYSIN = """//SYSINS JOB (T043JM,JM00,1,0,0,0),'SYSINS - JRM',CLASS=R, +// MSGCLASS=X,MSGLEVEL=1,NOTIFY=OMVSADM +//STEP1 EXEC PGM=BPXBATCH,PARM='SH sleep 1' +//STDOUT DD SYSOUT=* +//STDERR DD SYSOUT=* +//LISTCAT EXEC PGM=IDCAMS,REGION=4M +//SYSPRINT DD SYSOUT=* +//SYSIN DD * + LISTCAT ENTRIES('TEST.DATASET.JCL') ALL +/* +// +""" + TEMP_PATH = "/tmp/jcl" def test_zos_job_output_no_job_id(ansible_zos_module): @@ -149,8 +162,42 @@ def test_zos_job_output_job_exists_with_filtered_ddname(ansible_zos_module): hosts.all.file(path=TEMP_PATH, state="absent") +def test_zos_job_output_job_exists_with_sysin(ansible_zos_module): + try: + hosts = ansible_zos_module + hosts.all.file(path=TEMP_PATH, state="directory") + hosts.all.zos_data_set( + name="TEST.DATASET.JCL", + type="PS", + state="present" + ) + hosts.all.shell( + cmd=f"echo {quote(JCL_FILE_CONTENTS_SYSIN)} > {TEMP_PATH}/SYSIN" + ) + result = hosts.all.zos_job_submit( + src=f"{TEMP_PATH}/SYSIN", location="uss", volume=None + ) + hosts.all.file(path=TEMP_PATH, state="absent") + sysin = "True" + results = hosts.all.zos_job_output(job_name="SYSINS", input=sysin) + for result in results.contacted.values(): + print(result) + assert result.get("changed") is False + for job in result.get("jobs"): + assert len(job.get("ddnames")) >= 1 + sysin_found = False + for ddname_entry in job.get("ddnames"): + if ddname_entry.get("ddname") == "SYSIN": + sysin_found = True + break + assert sysin_found + finally: + hosts.all.zos_data_set(name="TEST.DATASET.JCL", state="absent") + hosts.all.file(path=TEMP_PATH, state="absent") + + def test_zos_job_submit_job_id_and_owner_included(ansible_zos_module): hosts = ansible_zos_module results = hosts.all.zos_job_output(job_id="STC00*", owner="MASTER") for result in results.contacted.values(): - assert result.get("jobs")[0].get("ret_code").get("msg_txt") is not None + assert result.get("jobs")[0].get("ret_code").get("msg_txt") is not None \ No newline at end of file diff --git a/tests/functional/modules/test_zos_job_submit_func.py b/tests/functional/modules/test_zos_job_submit_func.py index fca001a3e..d64487b00 100644 --- a/tests/functional/modules/test_zos_job_submit_func.py +++ b/tests/functional/modules/test_zos_job_submit_func.py @@ -425,7 +425,7 @@ async_status: jid: "{{{{ job_task.ansible_job_id }}}}" register: job_result - until: job_result.finished + until: job_result.finished | bool retries: 20 delay: 5 """ diff --git a/tests/functional/modules/test_zos_script_func.py b/tests/functional/modules/test_zos_script_func.py index 2afa75913..3a7359020 100644 --- a/tests/functional/modules/test_zos_script_func.py +++ b/tests/functional/modules/test_zos_script_func.py @@ -87,7 +87,7 @@ async_status: jid: "{{{{ job_task.ansible_job_id }}}}" register: job_result - until: job_result.finished + until: job_result.finished | bool retries: 20 delay: 5 """ diff --git a/tests/functional/modules/test_zos_unarchive_func.py b/tests/functional/modules/test_zos_unarchive_func.py index 22238326a..5b4aff3df 100644 --- a/tests/functional/modules/test_zos_unarchive_func.py +++ b/tests/functional/modules/test_zos_unarchive_func.py @@ -72,7 +72,7 @@ async_status: jid: "{{{{ job_task.ansible_job_id }}}}" register: job_result - until: job_result.finished + until: job_result.finished | bool retries: 20 delay: 5 """