LoginSignup
1
0

Ansibleを用いたz/OSカスタマイズ自動化 - (2) z/OS Core Collectionを試す

Last updated at Posted at 2024-06-23

はじめに

先の記事では、まず基本となる構成や単純なPlaybookを動かすところまでをやってみました。まずシンプルなPlaybookとしてMVSコマンドを投入するということをやってみましたが、他にも各種z/OS管理機能がAnsibleモジュールとして提供されています。
ここでは、z/OS Core Collectionで提供されるz/OS操作に関するモジュールを使ってどんなことができるのかを見ていきたいと思います。

関連記事

Ansibleを用いたz/OSカスタマイズ自動化 - (1) 概要/基本構成
Ansibleを用いたz/OSカスタマイズ自動化 - (2) z/OS Core Collectionを試す
Ansibleを用いたz/OSカスタマイズ自動化 - (3) Ansible提供モジュールを試す
Ansibleを用いたz/OSカスタマイズ自動化 - (4) 雑多な考察

Tips

モジュールの動作をトライ&エラーを繰り返して確認していく際には、デフォルトの設定だとPlaybook実行結果がかなり分かりにくいと感じることが多々あります。
参考:Ansible Tips: taskがエラーとなった場合などの結果出力を劇的に読みやすくする設定(stdout_callback = debug)
上の記事で紹介されている設定をするとかなり出力結果がみやすくなるのでおすすめです。

具体的には、ansible.cfgにて以下の設定を行います。

ansible.cfg
[defaults]
...
verbosity = 1
stdout_callback = ansible.posix.debug

verbosity = 1'指定により成功時にも詳細出力結果が表示されるようになり、かつstdout_callback = ansible.posix.debug`指定により出力結果がフォーマットされたJSON形式となります。

この機能を使用する際は前提として ansible.posix コレクションをインストールしておく必要があります。

参考:
ansible.posix.debug callback – formatted stdout/stderr display
ANSIBLE_VERBOSITY

※当記事の結果は基本的には上の設定を行った環境での例を記載しています。

z/OS Core Collection提供のモジュールを試す

ここでは具体的なオペレーションの例をあげてそれがz/OS Core Collectionで提供されるモジュールを使用してどのようにPlaybookを実装できるのかを見ていきたいと思います。

0.MVSコマンド実行

単純なMVSコマンド実行は前回の記事で実施してみたのでそちらを参照
サンプルPlaybook

1.TSOコマンド実行

zos_tso_commandというモジュールを使用してTSOコマンドを実行してみたいと思います。

ここではLISTCAT ENTRIES(xxx) というコマンドを例にやってみます。
※User Catalog作成前に存在チェックをする目的でこのコマンドを利用することを想定しています。

参考:PCOMから実行した時の例

コマンド成功例 (存在するEntryを指定)
image.png
image.png

コマンド失敗例 (存在しないEntryを指定)
image.png
image.png

参考:USS上のシェルからtsocmd経由で実行した時の例

コマンド成功例 (存在するEntryを指定)

実行例
IBMUSER : /u/ibmuser : > tsocmd "LISTCAT ENTRIES('CATALOG.VS01.TSO')"
LISTCAT ENTRIES('CATALOG.VS01.TSO')
USERCATALOG --- CATALOG.VS01.TSO
     IN-CAT --- CATALOG.VS01.MASTER

コマンド失敗例 (存在しないEntryを指定)

実行例
IBMUSER : /u/ibmuser : > tsocmd "LISTCAT ENTRIES('CATALOG.TEST01')"
LISTCAT ENTRIES('CATALOG.TEST01')
IDC3012I ENTRY CATALOG.TEST01 NOT FOUND+
IDC3009I ** VSAM CATALOG RETURN CODE IS 8 - REASON CODE IS IGG0CLEG-42
IDC1566I ** CATALOG.TEST01 NOT LISTED
IDC0014I LASTCC=4

上のようなTSOコマンドをAnsibleのPlaybookで実行してみます。

コマンド成功例 (存在するEntryを指定)

zos_check_ucat_test01.yml
- name: check user catalog
  hosts: ezdwazi04 
  environment: "{{ environment_vars }}"
  vars:
    ucatName: "CATALOG.VS01.TSO"
    #ucatName: "CATALOG.TEST01"

  gather_facts: no
  
  tasks:
    - name: check user catalog
      ibm.ibm_zos_core.zos_tso_command:
        commands:
          - LISTCAT ENTRIES('{{ ucatName }}')
      register: tmp_result
実行例
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_ansible_zos_sample_test$ ansible-playbook sample/zos_check_ucat_test01.yml 
Using /home/user01/Ansible/VSCode_workspace/my_ansible_zos_sample_test/ansible.cfg as config file

PLAY [check user catalog] ***************************************************************************************************************************************************************************************

TASK [check user catalog] ***************************************************************************************************************************************************************************************
changed: [ezdwazi04] => {
    "changed": true,
    "max_rc": 0,
    "output": [
        {
            "command": "LISTCAT ENTRIES('CATALOG.VS01.TSO')",
            "content": [
                "USERCATALOG --- CATALOG.VS01.TSO",
                "     IN-CAT --- CATALOG.VS01.MASTER",
                ""
            ],
            "failed": false,
            "lines": 3,
            "rc": 0,
            "stderr": ""
        }
    ]
}

PLAY RECAP ******************************************************************************************************************************************************************************************************
ezdwazi04                  : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

コマンド失敗例 (存在しないEntryを指定)

zos_check_ucat_test01.yml
- name: check user catalog
  hosts: ezdwazi04 
  environment: "{{ environment_vars }}"
  vars:
    #ucatName: "CATALOG.VS01.TSO"
    ucatName: "CATALOG.TEST01"

  gather_facts: no
  
  tasks:
    - name: check user catalog
      ibm.ibm_zos_core.zos_tso_command:
        commands:
          - LISTCAT ENTRIES('{{ ucatName }}')
      register: tmp_result
実行例
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_ansible_zos_sample_test$ ansible-playbook sample/zos_check_ucat_test01.yml 
Using /home/user01/Ansible/VSCode_workspace/my_ansible_zos_sample_test/ansible.cfg as config file

PLAY [check user catalog] ***************************************************************************************************************************************************************************************

TASK [check user catalog] ***************************************************************************************************************************************************************************************
fatal: [ezdwazi04]: FAILED! => {
    "changed": false,
    "max_rc": 0,
    "output": [
        {
            "command": "LISTCAT ENTRIES('CATALOG.TEST01')",
            "content": [
                "IDC3012I ENTRY CATALOG.TEST01 NOT FOUND+",
                "IDC3009I ** VSAM CATALOG RETURN CODE IS 8 - REASON CODE IS IGG0CLEG-42",
                "IDC1566I ** CATALOG.TEST01 NOT LISTED",
                "IDC0014I LASTCC=4",
                ""
            ],
            "failed": true,
            "lines": 5,
            "rc": 4,
            "stderr": ""
        }
    ]
}

MSG:

Some (True) command(s) failed:
Command "LISTCAT ENTRIES('CATALOG.TEST01')" executionfailed.  RC was 4.

PLAY RECAP ******************************************************************************************************************************************************************************************************
ezdwazi04                  : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0  

さて、次にこのTSOコマンドの結果をPlaybook上でハンドリングしてみたいと思います。
※User Catalogが存在していなかった場合にのみ作成するなどの判断を行うことを想定します。

zos_check_ucat_test02.yml
- name: check user catalog
  hosts: ezdwazi04 
  environment: "{{ environment_vars }}"
  vars:
    #ucatName: "CATALOG.VS01.TSO"
    ucatName: "CATALOG.TEST01"

  gather_facts: no
  
  tasks:
    - name: check user catalog
      ibm.ibm_zos_core.zos_tso_command:
        commands:
          - LISTCAT ENTRIES('{{ ucatName }}')
      register: tmp_result
      ignore_errors: yes

    - name: display results 1
      debug: 
        msg: "result: {{ tmp_result }} "

    - name: display results 2
      debug: 
        msg: "output: {{ tmp_result['output'] }}"

    - name: display results 3
      debug: 
        msg: "rc: {{ tmp_result.output[0].rc }} / stderr: {{ tmp_result.output[0].stderr }}  / failed: {{ tmp_result.output[0].failed }} "
        
    - name: define user catalog when it does not exist
      debug:
        msg: "*** define user catalog ***"
      when: tmp_result.output[0].failed == True

Playbook補足:

  • TSOコマンド実行のタスク(check user catalog)に、ignore_errors: yesを追加で指定しています。コマンドがエラーになるとデフォルトではPlaybook自体の処理が終了して後続タスクが実行されません。後続タスクでエラー時の判断をするためにこの指定をしてエラーを無視して処理継続できるようにしています。
  • display results 1~3 のタスクを指定して、TSOコマンド結果を保持した変数 tmp_resultの内容をいくつかのパターンで表示しています。
  • 最後のタスクでは、whenを用いてTSOコマンド結果の成否を判断し、失敗した場合のみメッセージを出力させているということやっています。
実行例
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_ansible_zos_sample_test$ ansible-playbook sample/zos_check_ucat_test02.yml 
Using /home/user01/Ansible/VSCode_workspace/my_ansible_zos_sample_test/ansible.cfg as config file

PLAY [check user catalog] ***************************************************************************************************************************************************************************************

TASK [check user catalog] ***************************************************************************************************************************************************************************************
fatal: [ezdwazi04]: FAILED! => {
    "changed": false,
    "max_rc": 0,
    "output": [
        {
            "command": "LISTCAT ENTRIES('CATALOG.TEST01')",
            "content": [
                "IDC3012I ENTRY CATALOG.TEST01 NOT FOUND+",
                "IDC3009I ** VSAM CATALOG RETURN CODE IS 8 - REASON CODE IS IGG0CLEG-42",
                "IDC1566I ** CATALOG.TEST01 NOT LISTED",
                "IDC0014I LASTCC=4",
                ""
            ],
            "failed": true,
            "lines": 5,
            "rc": 4,
            "stderr": ""
        }
    ]
}

MSG:

Some (True) command(s) failed:
Command "LISTCAT ENTRIES('CATALOG.TEST01')" executionfailed.  RC was 4.
...ignoring

TASK [display results 1] ****************************************************************************************************************************************************************************************
ok: [ezdwazi04] => {}

MSG:

result: {'changed': False, 'failed': True, 'output': [{'command': "LISTCAT ENTRIES('CATALOG.TEST01')", 'rc': 4, 'content': ['IDC3012I ENTRY CATALOG.TEST01 NOT FOUND+', 'IDC3009I ** VSAM CATALOG RETURN CODE IS 8 - REASON CODE IS IGG0CLEG-42', 'IDC1566I ** CATALOG.TEST01 NOT LISTED', 'IDC0014I LASTCC=4', ''], 'lines': 5, 'stderr': '', 'failed': True}], 'max_rc': 0, 'msg': 'Some (True) command(s) failed:\nCommand "LISTCAT ENTRIES(\'CATALOG.TEST01\')" executionfailed.  RC was 4.'} 

TASK [display results 2] ****************************************************************************************************************************************************************************************
ok: [ezdwazi04] => {}

MSG:

output: [{'command': "LISTCAT ENTRIES('CATALOG.TEST01')", 'rc': 4, 'content': ['IDC3012I ENTRY CATALOG.TEST01 NOT FOUND+', 'IDC3009I ** VSAM CATALOG RETURN CODE IS 8 - REASON CODE IS IGG0CLEG-42', 'IDC1566I ** CATALOG.TEST01 NOT LISTED', 'IDC0014I LASTCC=4', ''], 'lines': 5, 'stderr': '', 'failed': True}]

TASK [display results 3] ****************************************************************************************************************************************************************************************
ok: [ezdwazi04] => {}

MSG:

rc: 4 / stderr:   / failed: True 

TASK [define user catalog when it does not exist] ***************************************************************************************************************************************************************
ok: [ezdwazi04] => {}

MSG:

*** define user catalog ***

PLAY RECAP ******************************************************************************************************************************************************************************************************
ezdwazi04                  : ok=5    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=1   

TSOコマンド実行し、その結果によって処理を制御する基本的なやり方が確認できました。

2.Jinja2テンプレートを使用したJCL実行

zos_job_submit というモジュールを使います。

AnsibleにはJinja2テンプレートという仕組みが合って、スクリプトや各種構成ファイルなどをテンプレートとして作成しておき、一部の内容を環境に応じて動的に変更して利用することができます。
ここでは、JCLをJinja2テンプレートとして作成しておき、一部のパラメーターを環境変数で与えて実行する、ということをやってみます。

テンプレートとなるJCLを作成:

templates/listc.jcl
//LISTC    JOB   MSGCLASS=X,CLASS=A                       
//*-----------------------------------------------------  
//*                                                       
//GO       EXEC PGM=IDCAMS,REGION=0M                      
//SYSPRINT DD SYSOUT=*                                    
//SYSIN DD *                                              
 LISTCAT ENTRIES({{ listcEntryName }})                                
/*        

templateというディレクトリ下に上のようなjCLのテンプレートを作成します。上の例はIDCAMSユーティリティでLISTCATを実行する単純なJCLです。
LISTCAT ENTRIES(xxx) のxxx部分を変数に置き換えています。このようにJinja2の作法に従って動的に文字列を置き換えるための記述が行えます(if分を用いた複雑な条件分岐なども利用できます)。

上のtemplateを使用してJCLをSubmitするためのPlaybookを作成します。

zos_jcl_listc_test01.yml
- name: submit a JCL
  hosts: ezdwazi04 
  environment: "{{ environment_vars }}"
  gather_facts: no
  vars:
    jclName: "listc.jcl"
    listcEntryName: "CATALOG.VS01.TSO"
    #listcEntryName: "CATALOG.TEST01"
    #listcEntryName: "TTTTTTTTTTTTTTT"
  
  tasks:
    
    - name: submit JCL
      ibm.ibm_zos_core.zos_job_submit:
        src: "./templates/{{ jclName }}"
        location: LOCAL
        encoding:
          from: UTF-8
          to: IBM-037
        use_template: true
        wait_time_s: 10
      register: jcl_result
      ignore_errors: yes

    - name: display results when rc > 4
      debug:
        msg: "Result: {{ jcl_result.jobs[0].job_id }} / {{ jcl_result.jobs[0].ret_code }}"
      when: jcl_result.jobs[0].ret_code.code > 4

ここでは、JCLがRC=0で正常終了する場合、RC=4でエラーになる場合、RC=12でエラーになる場合の結果を見てみます。

JCLがRC=0で終了する例 (存在するEntryを指定)

実行例
実行例
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_ansible_zos_sample_test$ ansible-playbook sample/zos_jcl_listc_test01.yml 
Using /home/user01/Ansible/VSCode_workspace/my_ansible_zos_sample_test/ansible.cfg as config file

PLAY [submit a JCL] *********************************************************************************************************************************************************************************************

TASK [submit JCL] ***********************************************************************************************************************************************************************************************
changed: [ezdwazi04] => {
    "changed": true,
    "dest": "/tmp/ansible.zbtbqktz",
    "duration": 1,
    "gid": 0,
    "group": "SYS1",
    "is_binary": false,
    "jobs": [
        {
            "asid": "0",
            "class": "A",
            "content_type": "",
            "creation_date": "2024-03-31",
            "creation_time": "1:38:47",
            "ddnames": [
                {
                    "byte_count": "133",
                    "content": [
                        "1                         J E S 2  J O B  L O G  --  S Y S T E M  V S 0 1  --  N O D E  N 1",
                        "0     ",
                        " 01.38.47 JOB00283 ---- SUNDAY,    31 MAR 2024 ----",
                        " 01.38.47 JOB00283  IRR010I  USERID IBMUSER  IS ASSIGNED TO THIS JOB.",
                        " 01.38.47 JOB00283  ICH70001I IBMUSER  LAST ACCESS AT 01:38:46 ON SUNDAY, MARCH 31, 2024",
                        " 01.38.47 JOB00283  $HASP373 LISTC    STARTED - INIT 6    - CLASS A        - SYS VS01",
                        " 01.38.47 JOB00283  IEF403I LISTC - STARTED - TIME=01.38.47",
                        " 01.38.47 JOB00283  IEF404I LISTC - ENDED - TIME=01.38.47",
                        " 01.38.47 JOB00283  $HASP395 LISTC    ENDED - RC=0000",
                        "0------ JES2 JOB STATISTICS ------",
                        "-  31 MAR 2024 JOB EXECUTION DATE",
                        "-            8 CARDS READ",
                        "-           66 SYSOUT PRINT RECORDS",
                        "-            0 SYSOUT PUNCH RECORDS",
                        "-            7 SYSOUT SPOOL KBYTES",
                        "-         0.00 MINUTES EXECUTION TIME"
                    ],
                    "ddname": "JESMSGLG",
                    "id": "2",
                    "proctep": null,
                    "record_count": "16",
                    "stepname": "JES2"
                },
                {
                    "byte_count": "136",
                    "content": [
                        "        1 //LISTC    JOB   MSGCLASS=X,CLASS=A                                     JOB00283",
                        "          //*-----------------------------------------------------",
                        "          //*"
                    ],
                    "ddname": "JESJCL",
                    "id": "3",
                    "proctep": null,
                    "record_count": "3",
                    "stepname": "JES2"
                },
                {
                    "byte_count": "137",
                    "content": [
                        " ICH70001I IBMUSER  LAST ACCESS AT 01:38:46 ON SUNDAY, MARCH 31, 2024",
                        " IEFA111I LISTC IS USING THE FOLLOWING JOB RELATED SETTINGS:",
                        "          SWA=BELOW,TIOT SIZE=64K,DSENQSHR=DISALLOW,GDGBIAS=JOB",
                        " IEF236I ALLOC. FOR LISTC GO",
                        " IEF237I JES2 ALLOCATED TO SYSPRINT",
                        " IEF237I JES2 ALLOCATED TO SYSIN",
                        " IEF142I LISTC GO - STEP WAS EXECUTED - COND CODE 0000",
                        " IEF285I   IBMUSER.LISTC.JOB00283.D0000102.?            SYSOUT",
                        " IEF285I   IBMUSER.LISTC.JOB00283.D0000101.?            SYSIN",
                        " IEF373I STEP/GO      /START 2024091.0138",
                        " IEF032I STEP/GO      /STOP  2024091.0138",
                        "         CPU:     0 HR  00 MIN  00.00 SEC    SRB:     0 HR  00 MIN  00.00 SEC",
                        "         VIRT:    80K  SYS:   312K  EXT:       24K  SYS:     9880K",
                        "         ATB- REAL:                   228K  SLOTS:                     0K",
                        "              VIRT- ALLOC:      13M SHRD:       0M",
                        " IEF375I  JOB/LISTC   /START 2024091.0138",
                        " IEF033I  JOB/LISTC   /STOP  2024091.0138",
                        "         CPU:     0 HR  00 MIN  00.00 SEC    SRB:     0 HR  00 MIN  00.00 SEC"
                    ],
                    "ddname": "JESYSMSG",
                    "id": "4",
                    "proctep": null,
                    "record_count": "18",
                    "stepname": "JES2"
                },
                {
                    "byte_count": "125",
                    "content": [
                        "1IDCAMS  SYSTEM SERVICES                                           TIME: 01:38:47        03/31/24     PAGE      1",
                        "0     ",
                        "  LISTCAT ENTRIES(CATALOG.VS01.TSO)",
                        "0USERCATALOG --- CATALOG.VS01.TSO",
                        "      IN-CAT --- CATALOG.VS01.MASTER",
                        "1IDCAMS  SYSTEM SERVICES                                           TIME: 01:38:47        03/31/24     PAGE      2",
                        "0         THE NUMBER OF ENTRIES PROCESSED WAS:",
                        "                    AIX -------------------0",
                        "                    ALIAS -----------------0",
                        "                    CLUSTER ---------------0",
                        "                    DATA ------------------0",
                        "                    GDG -------------------0",
                        "                    INDEX -----------------0",
                        "                    NONVSAM ---------------0",
                        "                    PAGESPACE -------------0",
                        "                    PATH ------------------0",
                        "                    SPACE -----------------0",
                        "                    USERCATALOG -----------1",
                        "                    TAPELIBRARY -----------0",
                        "                    TAPEVOLUME ------------0",
                        "                    TOTAL -----------------1",
                        "0         THE NUMBER OF PROTECTED ENTRIES SUPPRESSED WAS 0",
                        "0IDC0001I FUNCTION COMPLETED, HIGHEST CONDITION CODE WAS 0",
                        "0     ",
                        "0IDC0002I IDCAMS PROCESSING COMPLETE. MAXIMUM CONDITION CODE WAS 0"
                    ],
                    "ddname": "SYSPRINT",
                    "id": "102",
                    "proctep": null,
                    "record_count": "25",
                    "stepname": "GO"
                }
            ],
            "duration": 1,
            "job_class": "A",
            "job_id": "JOB00283",
            "job_name": "LISTC",
            "owner": "IBMUSER",
            "priority": "1",
            "program_name": "IDCAMS",
            "queue_position": "37",
            "ret_code": {
                "code": 0,
                "msg": "CC",
                "msg_code": "0000",
                "msg_txt": "CC",
                "steps": [
                    {
                        "step_cc": 0,
                        "step_name": "GO"
                    }
                ]
            },
            "subsystem": "N1",
            "svc_class": "?",
            "system": "VS01"
        }
    ],
    "mode": "0600",
    "owner": "BPXROOT",
    "size": 479,
    "src": "/u/ibmuser/.ansible/tmp/ansible-tmp-1711863494.0475485-28656-208523992640196/source",
    "state": "file",
    "uid": 0
}

TASK [display results when rc > 4] ******************************************************************************************************************************************************************************
skipping: [ezdwazi04] => {
    "false_condition": "jcl_result.jobs[0].ret_code.code > 4"
}

PLAY RECAP ******************************************************************************************************************************************************************************************************
ezdwazi04                  : ok=1    changed=1    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   

JCLがRC=4で終了する例 (存在しないEntryを指定)

実行例
実行例
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_ansible_zos_sample_test$ ansible-playbook sample/zos_jcl_listc_test01.yml 
Using /home/user01/Ansible/VSCode_workspace/my_ansible_zos_sample_test/ansible.cfg as config file

PLAY [submit a JCL] *********************************************************************************************************************************************************************************************

TASK [submit JCL] ***********************************************************************************************************************************************************************************************
fatal: [ezdwazi04]: FAILED! => {
    "changed": false,
    "dest": "/tmp/ansible.n7mzkj6d",
    "duration": 1,
    "gid": 0,
    "group": "SYS1",
    "is_binary": false,
    "jobs": [
        {
            "asid": "0",
            "class": "A",
            "content_type": "",
            "creation_date": "2024-03-31",
            "creation_time": "1:41:25",
            "ddnames": [
                {
                    "byte_count": "133",
                    "content": [
                        "1                         J E S 2  J O B  L O G  --  S Y S T E M  V S 0 1  --  N O D E  N 1",
                        "0     ",
                        " 01.41.25 JOB00285 ---- SUNDAY,    31 MAR 2024 ----",
                        " 01.41.25 JOB00285  IRR010I  USERID IBMUSER  IS ASSIGNED TO THIS JOB.",
                        " 01.41.25 JOB00285  ICH70001I IBMUSER  LAST ACCESS AT 01:41:24 ON SUNDAY, MARCH 31, 2024",
                        " 01.41.25 JOB00285  $HASP373 LISTC    STARTED - INIT 6    - CLASS A        - SYS VS01",
                        " 01.41.25 JOB00285  IEF403I LISTC - STARTED - TIME=01.41.25",
                        " 01.41.25 JOB00285  IEF404I LISTC - ENDED - TIME=01.41.25",
                        " 01.41.25 JOB00285  $HASP395 LISTC    ENDED - RC=0004",
                        "0------ JES2 JOB STATISTICS ------",
                        "-  31 MAR 2024 JOB EXECUTION DATE",
                        "-            8 CARDS READ",
                        "-           67 SYSOUT PRINT RECORDS",
                        "-            0 SYSOUT PUNCH RECORDS",
                        "-            7 SYSOUT SPOOL KBYTES",
                        "-         0.00 MINUTES EXECUTION TIME"
                    ],
                    "ddname": "JESMSGLG",
                    "id": "2",
                    "proctep": null,
                    "record_count": "16",
                    "stepname": "JES2"
                },
                {
                    "byte_count": "136",
                    "content": [
                        "        1 //LISTC    JOB   MSGCLASS=X,CLASS=A                                     JOB00285",
                        "          //*-----------------------------------------------------",
                        "          //*"
                    ],
                    "ddname": "JESJCL",
                    "id": "3",
                    "proctep": null,
                    "record_count": "3",
                    "stepname": "JES2"
                },
                {
                    "byte_count": "137",
                    "content": [
                        " ICH70001I IBMUSER  LAST ACCESS AT 01:41:24 ON SUNDAY, MARCH 31, 2024",
                        " IEFA111I LISTC IS USING THE FOLLOWING JOB RELATED SETTINGS:",
                        "          SWA=BELOW,TIOT SIZE=64K,DSENQSHR=DISALLOW,GDGBIAS=JOB",
                        " IEF236I ALLOC. FOR LISTC GO",
                        " IEF237I JES2 ALLOCATED TO SYSPRINT",
                        " IEF237I JES2 ALLOCATED TO SYSIN",
                        " IEF142I LISTC GO - STEP WAS EXECUTED - COND CODE 0004",
                        " IEF285I   IBMUSER.LISTC.JOB00285.D0000102.?            SYSOUT",
                        " IEF285I   IBMUSER.LISTC.JOB00285.D0000101.?            SYSIN",
                        " IEF373I STEP/GO      /START 2024091.0141",
                        " IEF032I STEP/GO      /STOP  2024091.0141",
                        "         CPU:     0 HR  00 MIN  00.00 SEC    SRB:     0 HR  00 MIN  00.00 SEC",
                        "         VIRT:    80K  SYS:   308K  EXT:       16K  SYS:     9816K",
                        "         ATB- REAL:                   228K  SLOTS:                     0K",
                        "              VIRT- ALLOC:      13M SHRD:       0M",
                        " IEF375I  JOB/LISTC   /START 2024091.0141",
                        " IEF033I  JOB/LISTC   /STOP  2024091.0141",
                        "         CPU:     0 HR  00 MIN  00.00 SEC    SRB:     0 HR  00 MIN  00.00 SEC"
                    ],
                    "ddname": "JESYSMSG",
                    "id": "4",
                    "proctep": null,
                    "record_count": "18",
                    "stepname": "JES2"
                },
                {
                    "byte_count": "125",
                    "content": [
                        "1IDCAMS  SYSTEM SERVICES                                           TIME: 01:41:25        03/31/24     PAGE      1",
                        "0     ",
                        "  LISTCAT ENTRIES(CATALOG.TEST01)",
                        "0IDC3012I ENTRY CATALOG.TEST01 NOT FOUND",
                        " IDC3009I ** VSAM CATALOG RETURN CODE IS 8 - REASON CODE IS IGG0CLEG-42",
                        " IDC1566I ** CATALOG.TEST01 NOT LISTED",
                        "1IDCAMS  SYSTEM SERVICES                                           TIME: 01:41:25        03/31/24     PAGE      2",
                        "0         THE NUMBER OF ENTRIES PROCESSED WAS:",
                        "0                   AIX -------------------0",
                        "                    ALIAS -----------------0",
                        "                    CLUSTER ---------------0",
                        "                    DATA ------------------0",
                        "                    GDG -------------------0",
                        "                    INDEX -----------------0",
                        "                    NONVSAM ---------------0",
                        "                    PAGESPACE -------------0",
                        "                    PATH ------------------0",
                        "                    SPACE -----------------0",
                        "                    USERCATALOG -----------0",
                        "                    TAPELIBRARY -----------0",
                        "                    TAPEVOLUME ------------0",
                        "                    TOTAL -----------------0",
                        "0         THE NUMBER OF PROTECTED ENTRIES SUPPRESSED WAS 0",
                        "0IDC0001I FUNCTION COMPLETED, HIGHEST CONDITION CODE WAS 4",
                        "0     ",
                        "0IDC0002I IDCAMS PROCESSING COMPLETE. MAXIMUM CONDITION CODE WAS 4"
                    ],
                    "ddname": "SYSPRINT",
                    "id": "102",
                    "proctep": null,
                    "record_count": "26",
                    "stepname": "GO"
                }
            ],
            "duration": 1,
            "job_class": "A",
            "job_id": "JOB00285",
            "job_name": "LISTC",
            "owner": "IBMUSER",
            "priority": "1",
            "program_name": "IDCAMS",
            "queue_position": "38",
            "ret_code": {
                "code": 4,
                "msg": "CC",
                "msg_code": "0004",
                "msg_txt": "The job return code 4 was non-zero in the job output, this job has failed.",
                "steps": [
                    {
                        "step_cc": 4,
                        "step_name": "GO"
                    }
                ]
            },
            "subsystem": "N1",
            "svc_class": "?",
            "system": "VS01"
        }
    ],
    "mode": "0600",
    "owner": "BPXROOT",
    "size": 477,
    "src": "/u/ibmuser/.ansible/tmp/ansible-tmp-1711863652.3322203-29040-37539778899676/source",
    "state": "file",
    "uid": 0
}

MSG:

The JCL submitted with job id JOB00285 but there was an error, please review the error for further details: The job return code 4 was non-zero in the job output, this job has failed.
...ignoring

TASK [display results when rc > 4] ******************************************************************************************************************************************************************************
skipping: [ezdwazi04] => {
    "false_condition": "jcl_result.jobs[0].ret_code.code > 4"
}

PLAY RECAP ******************************************************************************************************************************************************************************************************
ezdwazi04                  : ok=1    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=1   

JCLがRC=12で終了する例 (不当なEntryを指定)

実行例
実行例
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_ansible_zos_sample_test$ ansible-playbook sample/zos_jcl_listc_test01.yml 
Using /home/user01/Ansible/VSCode_workspace/my_ansible_zos_sample_test/ansible.cfg as config file

PLAY [submit a JCL] *********************************************************************************************************************************************************************************************

TASK [submit JCL] ***********************************************************************************************************************************************************************************************
fatal: [ezdwazi04]: FAILED! => {
    "changed": false,
    "dest": "/tmp/ansible.mwolmtkq",
    "duration": 1,
    "gid": 0,
    "group": "SYS1",
    "is_binary": false,
    "jobs": [
        {
            "asid": "0",
            "class": "A",
            "content_type": "",
            "creation_date": "2024-03-31",
            "creation_time": "1:42:20",
            "ddnames": [
                {
                    "byte_count": "133",
                    "content": [
                        "1                         J E S 2  J O B  L O G  --  S Y S T E M  V S 0 1  --  N O D E  N 1",
                        "0     ",
                        " 01.42.20 JOB00286 ---- SUNDAY,    31 MAR 2024 ----",
                        " 01.42.20 JOB00286  IRR010I  USERID IBMUSER  IS ASSIGNED TO THIS JOB.",
                        " 01.42.21 JOB00286  ICH70001I IBMUSER  LAST ACCESS AT 01:42:20 ON SUNDAY, MARCH 31, 2024",
                        " 01.42.21 JOB00286  $HASP373 LISTC    STARTED - INIT 6    - CLASS A        - SYS VS01",
                        " 01.42.21 JOB00286  IEF403I LISTC - STARTED - TIME=01.42.21",
                        " 01.42.21 JOB00286  IEF404I LISTC - ENDED - TIME=01.42.21",
                        " 01.42.21 JOB00286  $HASP395 LISTC    ENDED - RC=0012",
                        "0------ JES2 JOB STATISTICS ------",
                        "-  31 MAR 2024 JOB EXECUTION DATE",
                        "-            8 CARDS READ",
                        "-           48 SYSOUT PRINT RECORDS",
                        "-            0 SYSOUT PUNCH RECORDS",
                        "-            6 SYSOUT SPOOL KBYTES",
                        "-         0.00 MINUTES EXECUTION TIME"
                    ],
                    "ddname": "JESMSGLG",
                    "id": "2",
                    "proctep": null,
                    "record_count": "16",
                    "stepname": "JES2"
                },
                {
                    "byte_count": "136",
                    "content": [
                        "        1 //LISTC    JOB   MSGCLASS=X,CLASS=A                                     JOB00286",
                        "          //*-----------------------------------------------------",
                        "          //*"
                    ],
                    "ddname": "JESJCL",
                    "id": "3",
                    "proctep": null,
                    "record_count": "3",
                    "stepname": "JES2"
                },
                {
                    "byte_count": "137",
                    "content": [
                        " ICH70001I IBMUSER  LAST ACCESS AT 01:42:20 ON SUNDAY, MARCH 31, 2024",
                        " IEFA111I LISTC IS USING THE FOLLOWING JOB RELATED SETTINGS:",
                        "          SWA=BELOW,TIOT SIZE=64K,DSENQSHR=DISALLOW,GDGBIAS=JOB",
                        " IEF236I ALLOC. FOR LISTC GO",
                        " IEF237I JES2 ALLOCATED TO SYSPRINT",
                        " IEF237I JES2 ALLOCATED TO SYSIN",
                        " IEF142I LISTC GO - STEP WAS EXECUTED - COND CODE 0012",
                        " IEF285I   IBMUSER.LISTC.JOB00286.D0000102.?            SYSOUT",
                        " IEF285I   IBMUSER.LISTC.JOB00286.D0000101.?            SYSIN",
                        " IEF373I STEP/GO      /START 2024091.0142",
                        " IEF032I STEP/GO      /STOP  2024091.0142",
                        "         CPU:     0 HR  00 MIN  00.00 SEC    SRB:     0 HR  00 MIN  00.00 SEC",
                        "         VIRT:    76K  SYS:   308K  EXT:       16K  SYS:     9800K",
                        "         ATB- REAL:                   228K  SLOTS:                     0K",
                        "              VIRT- ALLOC:      13M SHRD:       0M",
                        " IEF375I  JOB/LISTC   /START 2024091.0142",
                        " IEF033I  JOB/LISTC   /STOP  2024091.0142",
                        "         CPU:     0 HR  00 MIN  00.00 SEC    SRB:     0 HR  00 MIN  00.00 SEC"
                    ],
                    "ddname": "JESYSMSG",
                    "id": "4",
                    "proctep": null,
                    "record_count": "18",
                    "stepname": "JES2"
                },
                {
                    "byte_count": "125",
                    "content": [
                        "1IDCAMS  SYSTEM SERVICES                                           TIME: 01:42:21        03/31/24     PAGE      1",
                        "0     ",
                        "  LISTCAT ENTRIES(TTTTTTTTTTTTTTT)",
                        "0IDC3203I ITEM 'TTTTTTTTTTTTTTT' DOES NOT ADHERE TO RESTRICTIONS",
                        "0IDC3202I ABOVE TEXT BYPASSED UNTIL NEXT COMMAND. CONDITION CODE IS 12",
                        "0     ",
                        "0IDC0002I IDCAMS PROCESSING COMPLETE. MAXIMUM CONDITION CODE WAS 12"
                    ],
                    "ddname": "SYSPRINT",
                    "id": "102",
                    "proctep": null,
                    "record_count": "7",
                    "stepname": "GO"
                }
            ],
            "duration": 1,
            "job_class": "A",
            "job_id": "JOB00286",
            "job_name": "LISTC",
            "owner": "IBMUSER",
            "priority": "1",
            "program_name": "IDCAMS",
            "queue_position": "39",
            "ret_code": {
                "code": 12,
                "msg": "CC",
                "msg_code": "0012",
                "msg_txt": "The job return code 12 was non-zero in the job output, this job has failed.",
                "steps": [
                    {
                        "step_cc": 12,
                        "step_name": "GO"
                    }
                ]
            },
            "subsystem": "N1",
            "svc_class": "?",
            "system": "VS01"
        }
    ],
    "mode": "0600",
    "owner": "BPXROOT",
    "size": 478,
    "src": "/u/ibmuser/.ansible/tmp/ansible-tmp-1711863707.2490025-29245-262516146451725/source",
    "state": "file",
    "uid": 0
}

MSG:

The JCL submitted with job id JOB00286 but there was an error, please review the error for further details: The job return code 12 was non-zero in the job output, this job has failed.
...ignoring

TASK [display results when rc > 4] ******************************************************************************************************************************************************************************
ok: [ezdwazi04] => {}

MSG:

Result: JOB00286 / {'msg': 'CC', 'msg_code': '0012', 'code': 12, 'msg_txt': 'The job return code 12 was non-zero in the job output, this job has failed.', 'steps': [{'step_name': 'GO', 'step_cc': 12}]}

PLAY RECAP ******************************************************************************************************************************************************************************************************
ezdwazi04                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=1   

JCLの場合一般的にRC=0, RC=4は許容される結果と言えますが、AnsibleのタスクとしてはRC=0以外はエラーと判断されてしまいます。
Playbook上の最後のタスクは、JCLのReturn Codeの数値を判定し4より大きい場合のみJCL結果の一部の情報を出力するようにします。そのため最初の2つのケース(RC=0, RC=4)では最後のメッセージ出力のタスクはスキップされ、最後のケース(RC=12)の場合のみ実行される結果となっています。

ログを見てみると、Jinja2により解釈されたテンプレートが一旦USS上のテンポラリーのディレクトリ(/u/ibmuser/.ansible/tmp/...:: SSHSユーザーのホーム以下) に転送され、それがSubmitされるという処理が内部的に行われているものと思われます。

3.データセットのダンプ/リストア

zos_backup_restoreというモジュールを使います。
このモジュールは大きく2種類のオペレーションが行えます。1つはデータセットの「ダンプ」(backup)で、もう1つはダンプされたデータセットの「リストア」(restore)です。それぞれのオペレーションはパラメーターを切り替えることで使い分けます。
このモジュールでは内部的にADRDSSUによるダンプ/リストアが行われますが、それだけでなくAMATERSEによる圧縮/解凍も合わせて行ってくれます。従って、これを利用してある環境から取得したダンプ/圧縮した情報を別の環境にコピーして解凍/リストアすることができます。

ここでは、ある環境で適当なデータセットを選択して論理ダンプ/圧縮し、別の環境で解凍/リストアする、ということを試してみたいと思います。

論理ダンプ取得

Wazi aaSのStock Imageに含まれるCICS関連のデータセットのうちの一部を適当に抜粋して論理ダンプを取得してみます。

CICS関連データセット抜粋
 DSLIST - Data Sets Matching CICSTS61                              Row 45 of 64 
                                                                                
 Command - Enter "/" to select action                  Message           Volume 
 -------------------------------------------------------------------------------
          CICSTS61.DFHCSD                                                *VSAM* 
          CICSTS61.DFHCSD.DATA                                           USRVS1 
          CICSTS61.DFHCSD.INDEX                                          USRVS1 
          CICSTS61.SAMPLE.DFHAIPTH                                       *PATH* 
          CICSTS61.SAMPLE.DFHCTAIX                                       *AIX * 
          CICSTS61.SAMPLE.DFHCTAIX.DATA                                  USRVS1 
          CICSTS61.SAMPLE.DFHCTAIX.INDEX                                 USRVS1 
          CICSTS61.SAMPLE.DFHCTCUS                                       *VSAM* 
          CICSTS61.SAMPLE.DFHCTCUS.DATA                                  USRVS1 
          CICSTS61.SAMPLE.DFHCTCUS.INDEX                                 USRVS1 
          CICSTS61.SAMPLE.DFHCTHLP                                       *VSAM* 
          CICSTS61.SAMPLE.DFHCTHLP.DATA                                  USRVS1 
          CICSTS61.SAMPLE.DFHCTHLP.INDEX                                 USRVS1 
          CICSTS61.SYSIN                                                 USRVS1 
          CICSTS61.SYSIN.BK                                              USRVS1 
          CICSTS61.SYSIN.BK.TEST                                         USRVS1 
          CICSTS61.SYSIN2                                                USRVS1 

上のあたりの論理ダンプを取得するPlaybookを作成してみます。

zos_logical_dump.yml
- name: get a logical dump and download
  hosts: ezdwazi04 
  environment: "{{ environment_vars }}"
  gather_facts: no
  vars:
    datasetIncludeList:
      - CICSTS61.SYSIN*
      - CICSTS61.SYSIN.**
      - CICSTS61.*CSD
      - CICSTS61.SAMPLE.*
  
  tasks:

    - name: backup test01
      ibm.ibm_zos_core.zos_backup_restore:
        operation: backup
        data_sets:
          include: "{{ datasetIncludeList }}"
        backup_name: /tmp/my_backup.dzp
        overwrite: true

    - name: download test01
      ibm.ibm_zos_core.zos_fetch:
        src: /tmp/my_backup.dzp
        dest: ./backup/my_backup.dzp
        is_binary: true
        flat: true

1つ目のタスクで zos_backup_restoreモジュールのoperation: backupを指定することで、バックアップ取得(ダンプ取得)することを指示しています。data_setsのincludeで取得したいデータセットのリストを指定しています。ダンプ/圧縮した結果はUSS上の/tmp/my_backup.dzpに出力するようにしています。

2つ目のタスクでは、zos_fetchというモジュールを使用して、上で取得した圧縮ファイルをローカル(Ansible Control Node)にコピーしています。

実行例
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_ansible_zos_sample_test$ ansible-playbook sample/zos_logical_dump.yml 
Using /home/user01/Ansible/VSCode_workspace/my_ansible_zos_sample_test/ansible.cfg as config file

PLAY [get a logical dump and download] **************************************************************************************************************************************************************************

TASK [backup test01] ********************************************************************************************************************************************************************************************
changed: [ezdwazi04] => {
    "backup_name": "",
    "changed": true,
    "message": ""
}

TASK [download test01] ******************************************************************************************************************************************************************************************
changed: [ezdwazi04] => {
    "changed": true,
    "data_set_type": "USS",
    "dest": "/home/user01/Ansible/VSCode_workspace/my_ansible_zos_sample_test/sample/backup/my_backup.dzp",
    "file": "/tmp/my_backup.dzp",
    "is_binary": true
}

PLAY RECAP ******************************************************************************************************************************************************************************************************
ezdwazi04                  : ok=2    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0    

これで、論理ダンプ/圧縮したファイル my_backup.dzp が取得されました。

論理ダンプのリストア

上で取得されたファイル my_backup.dzp をリストアしてみます。
実際には別の環境にリストアすることを想定していますが、ここでは動作確認ということで、同じ環境の別のHLQにリストアしてみます。

リストア用のPlaybookを作成してみます。

zos_restore.yml
- name: upload and restore
  hosts: ezdwazi04 
  environment: "{{ environment_vars }}"
  gather_facts: no
  vars: 
    backupFile: my_backup.dzp
    storageClass: SCBASE
    targetHLQ: TTT
  
  tasks:
    - name: upload test01
      ibm.ibm_zos_core.zos_copy:
        src: ./sample/backup/{{ backupFile }}
        dest: /tmp/{{ backupFile }}
        is_binary: true
        force: true

    - name: restore test01
      ibm.ibm_zos_core.zos_backup_restore:
        operation: restore
        backup_name: /tmp/{{ backupFile }}
        overwrite: true
        sms_storage_class: "{{ storageClass }}"
        hlq: "{{ targetHLQ }}"

1つ目のタスクでは、zos_copyモジュールを使用して、先に取得したファイルをターゲット環境のUSS上にアップロードしています。

2つ目のタスクでは、zos_backup_restoreモジュールのoperation: restoreを指定することで、リストアすることを指示しています。hlq: xxx でターゲットとなるHLQを指定しています。これを指定していない場合はAnsibleから接続時に使用したユーザー名がHLQとなります。
※ADRDSSUユーティリティーを使用したRESTOREではデータセット名を変更するなど細かな制御が行えますが、Ansible経由でのRESTOREでは利用できる操作は限定的のようです。
参考: Logical data set restore

実行例
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_ansible_zos_sample_test$ ansible-playbook sample/zos_restore.yml 
Using /home/user01/Ansible/VSCode_workspace/my_ansible_zos_sample_test/ansible.cfg as config file

PLAY [upload and restore] ***************************************************************************************************************************************************************************************

TASK [upload test01] ********************************************************************************************************************************************************************************************
ok: [ezdwazi04] => {
    "changed": false,
    "dest": "/tmp/my_backup.dzp",
    "gid": 0,
    "group": "SYS1",
    "is_binary": true,
    "mode": "0644",
    "owner": "BPXROOT",
    "size": 101376,
    "src": "./sample/backup/my_backup.dzp",
    "state": "file",
    "uid": 0
}

TASK [restore test01] *******************************************************************************************************************************************************************************************
changed: [ezdwazi04] => {
    "backup_name": "",
    "changed": true,
    "message": ""
}

PLAY RECAP ******************************************************************************************************************************************************************************************************
ezdwazi04                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

リストアされたデータセットを確認してみます。

DSLIST - Data Sets Matching TTT                                    Row 1 of 16 
                                                                               
Command - Enter "/" to select action                  Message           Volume 
-------------------------------------------------------------------------------
         TTT.DFHCSD                                                     *VSAM* 
         TTT.DFHCSD.DATA                                                USRVS1 
         TTT.DFHCSD.INDEX                                               USRVS1 
         TTT.SAMPLE.DFHCTAIX                                            *VSAM* 
         TTT.SAMPLE.DFHCTAIX.DATA                                       USRVS1 
         TTT.SAMPLE.DFHCTAIX.INDEX                                      USRVS1 
         TTT.SAMPLE.DFHCTCUS                                            *VSAM* 
         TTT.SAMPLE.DFHCTCUS.DATA                                       USRVS1 
         TTT.SAMPLE.DFHCTCUS.INDEX                                      USRVS1 
         TTT.SAMPLE.DFHCTHLP                                            *VSAM* 
         TTT.SAMPLE.DFHCTHLP.DATA                                       USRVS1 
         TTT.SAMPLE.DFHCTHLP.INDEX                                      USRVS1 
         TTT.SYSIN                                                      USRVS1 
         TTT.SYSIN.BK                                                   USRVS1 
         TTT.SYSIN.BK.TEST                                              USRVS1 
         TTT.SYSIN2                                                     USRVS1 
***************************** End of Data Set list ****************************

論理ダンプされたデータセットがリストアされたことが確認できました。

※必要に応じてUser CatalogやAliasの作成は別途検討してください。
※ここではzos_backup_restoreモジュールで取得したbackupファイルをリストアしていますが、これは内部的にはADRDSSU、AMATERSEが使われていますので、これらのユーティリティーで取得されたbackupファイルであれば、zos_backup_restoreモジュールでリストアすることができます。

4.IPLPARM変更

IPL時に参照されるパラメータの一部を変更するということをやってみます。ここでは、Wazi aaSのStockImage環境を想定し、この環境のIEASYMxxで指定されている環境変数の設定を変更する、ということをやってみます。
IPL関連のパラメータを変更する場合、ヘタに変更するとIPLできなくなるリスクがありますので、既存の設定でもIPLできるよう保険をかけておくような形を意識してPlaybook作成するようにします。

変更前IPL関連情報

D IPLINFO                                                               
IEE254I  03.19.05 IPLINFO DISPLAY 867                                   
 SYSTEM IPLED AT 03.14.42 ON 04/02/2024                                 
 RELEASE z/OS 02.05.00    LICENSE = z/OS                                
 USED LOADK2 IN SYS0.IPLPARM ON 0DE28                                   
 ARCHLVL = 2   MTLSHARE = N                                             
 VALIDATED BOOT: NO                                                     
 IEASYM LIST = (00,K2)                                                  
 IEASYS LIST = (00) (OP)                                                
 IODF DEVICE: ORIGINAL(0DE28) CURRENT(0DE28)                            
 IPL DEVICE: ORIGINAL(0DE27) CURRENT(0DE27) VOLUME(D25VS1)              
 VM CPID = zHYPaaS                                                      
 VM UUID = 0E72B0AB-495B-4B84-90FE-0DE4A82DF91E                         
 VM NAME = k8s_ead8                                                     
 VM EXT NAME = k8s_ead8711ba2cc4d08a16fd37427f4f01a_02g7_0e72b0ab-495b  
               -4b84-90fe-0de4a82df91e                
SYS1.PARMLIB(IEASYM00)
SYSDEF
 SYMDEF(&JESNAME='VSIPLEX')
 SYMDEF(&OWNNODE='00')
 SYMDEF(&VTAMCFG='00')
 SYMDEF(&VTAMSA='00')
 SYSPARM(00)
 SYMDEF(&IZUPRM='00')
 SYMDEF(&COMMAND='00')
 SYMDEF(&CONSOLE='00')
 SYMDEF(&COUPLE='00')
 SYMDEF(&IDFPRM='00')
 SYMDEF(&IOEPRM='00')
 SYMDEF(&LPALST='00')
 SYMDEF(&OMVSPARM='00')
 SYMDEF(&OPT='00')
 SYMDEF(&PROGNME='00')
 SYMDEF(&SCHED='00')
SYSDEF SYSNAME(VS01)
 SYMDEF(&J2SPVL='JSPVS')
 SYMDEF(&J2CKVL1='JCKVS1')
 SYMDEF(&J2CKHLQ='VS01')
 SYMDEF(&IPADDR4A='10')
 SYMDEF(&IPADDR4B='1')
 SYMDEF(&IPADDR4C='1')
 SYMDEF(&IPADDR4D='2')
 SYMDEF(&IPROUT4A='10')
 SYMDEF(&IPROUT4B='1')
 SYMDEF(&IPROUT4C='1')
 SYMDEF(&IPROUT4D='0')
 SYMDEF(&IPDRUT4A='10')
 SYMDEF(&IPDRUT4B='1')
 SYMDEF(&IPDRUT4C='1')
 SYMDEF(&IPDRUT4D='1')
 SYMDEF(&NODE='63')
 SYMDEF(&SSCPID='5065')
 SYMDEF(&HOSTSA='4')
 SYMDEF(&NAMSVRA='10')
 SYMDEF(&NAMSVRB='1')
 SYMDEF(&NAMSVRC='1')
 SYMDEF(&NAMSVRD='1')
 SYMDEF(&J2CKVL2='')
 SYMDEF(&TCPHOSTNAME_='STD1')
 SYMDEF(&TCPDOMAIN_='IBM.COM')
 SYMDEF(&VPCVSIIPADDRESS.='10.1.1.2')
 SYMDEF(&CERTEXPIRE='2025-11-01')
 SYMDEF(&CNMDOMN='CNM01')
 SYMDEF(&CNMNETID='NETA')
 SYMDEF(&CNMTCPN='TCPIP')
/*  Begin Ansible Block Insert  */
SYMDEF(&CNMDOMN='CNM01')
SYMDEF(&CNMTCPN='TCPIP')
SYMDEF(&CNMNETID='EXPNET')
SYMDEF(&JES2STR_='(COLD,NOREQ)')
SYMDEF(&DBGMGR='IPL')
SYMDEF(&EQAPROF='IPL')
SYMDEF(&EQARMTD='IPL')
SYMDEF(&CICSTS61='IPL')
SYMDEF(&CSQ9MSTR='IPL')
SYMDEF(&CSQ9CHIN='IPL')
SYMDEF(&IMS15RL1='IPL')
SYMDEF(&IMS15CR1='IPL')
SYMDEF(&DBC1MSTR='IPL')
SYMDEF(&DBD1MSTR='IPL')
SYMDEF(&IZUANG1='IPL')
SYMDEF(&IZUSVR1='IPL')
SYMDEF(&JMON='IPL')
SYMDEF(&RSED='IPL')
SYMDEF(&RSEAPI='IPL')
SYMDEF(&DBB='IPL')
SYMDEF(&DBBS='IPL')
SYMDEF(&ZOSCSRV='IPL')
SYMDEF(&RESOLV_='RESSETUP')
/*  End Ansible Block Insert */

Wazi aaSのStockImageでは、各種サブシステムがある程度セットアップされた状態のz/OS環境が提供されています。各サブシステムはNetviewのスクリプトで起動/停止などが管理されていますが、IPL時に起動するかどうかはIEASYMxxに指定されているシンボルにより制御されます。

参考: Wazi aaS: クラウド上でのメインフレーム開発環境構築 - (5) Stock Iamge確認 - Netview関連

例えば SYMDEF(&CICSTS61='IPL') と指定することでIPL時に自動でCICSリージョンが起動するようになっています。「IPL」部分を「NOS」に変更するとIPL時に自動起動されなくなります。
ここでは、一部のサブシステムの自動起動用のシンボルを変更して不要なものはIPL時に自動起動させないようにする、というカスタマイズをAnsible Playbookで実装してみたいと思います。

このメンバーを直接編集してもよいですが、万が一この変更によりIPLがうまくいかなくなってしまった時のために、現行設定でもIPLできる手段を残すようにします。そのため、LOADxxも確認しておきます。

SYS0.IPLPARM(LOADK2)
IODF     00 PROV     DEFAULT  00                                 
INITSQA        0001M                                             
SYSCAT   OPEVS1143CCATALOG.VS01.MASTER                           
IEASYM   (00,K2)                                                 
SYSPLEX  LOCAL                                                   
PARMLIB  K2.PARMLIB                                   USRVS1 
PARMLIB  SYS1.PARMLIB                                            
PARMLIB  SYS1.PARMLIB.INSTALL                                    

変更方針

以下のようにカスタマイズするようにしたいと思います。

  1. 新規にカスタマイズ用のPARMLIBライブラリーを作成
  2. 変更対象のメンバー(IEASYM00)をカスタマイズ用ライブラリーにコピー
  3. LOADK2のバックアップを取得し、LOADK2にカスタマイズ用ライブラリーを上位コンカチ
  4. カスタマイズ用ライブラリー上のIEASYM00を変更

※Wazi aaSの場合デフォルトで使われるLOAD PARMは決まってしまっているので、基本的にはLOADK2が使用されます。ただ、明示的にLOAD PARM指定でIPLすることもできるので、LOADK2のバックアップを取得しておけば、最悪デフォルトのLOADK2でIPLできなくなった場合でもバックアップ取得したLOADxxでIPLすれば元の環境で起動できるはず。
参考: Wazi as a Service - Operating with the serial console

Playbook作成

さて、上のような方針でPlaybook作成してみます。

zos_parmlib_test01.yml
- name: modify parmlib
  hosts: ezdwazi04 
  environment: "{{ environment_vars }}"
  gather_facts: no
  vars: 
    oldParmlib: SYS1.PARMLIB
    newParmlib: SYS1.PARMLIB.TEST01
    storageClass: SCBASE
    LOADParmMember: SYS0.IPLPARM(LOADK2)
    LOADParmMemberBK: SYS0.IPLPARM(LOADK9)
    oldIEASYMMember: "{{ oldParmlib }}(IEASYM00)"
    newIEASYMMember: "{{ newParmlib }}(IEASYM00)"
    componentList: 
      - "&CICSTS61"
      - "&CSQ9MSTR"
      - "&CSQ9CHIN"
      - "&ZOSCSRV"
    IPLOption: "NOS"
  
  tasks:
    - name: Create new Parmlib dataset
      ibm.ibm_zos_core.zos_data_set:
        name: "{{ newParmlib }}"
        type: PDS
        space_type: TRK
        space_primary: 45
        space_secondary: 15
        sms_storage_class: "{{ storageClass }}"
    - name: copy parmlib dataset
      ibm.ibm_zos_core.zos_copy:
        src: "{{ oldIEASYMMember }}"
        dest: "{{ newIEASYMMember }}"
        remote_src: true
        force: true
    - name: add PARMLIB library to LOADxx
      ibm.ibm_zos_core.zos_blockinfile:
        src: "{{ LOADParmMember }}"
        insertbefore: "^.*PARMLIB  {{ oldParmlib }}[^.]"
        block: |
          PARMLIB  {{ newParmlib }}
        marker: "* {mark} ANSIBLE MANAGED BLOCK"
        backup: true
        backup_name: "{{ LOADParmMemberBK }}"
    - name: replace IPL option
      ibm.ibm_zos_core.zos_lineinfile:
        src: "{{ newIEASYMMember }}"
        backrefs: true
        regexp: "^SYMDEF[(]{{ item }}="
        line: "SYMDEF({{ item }}='{{ IPLOption }}')"
        force: true
      loop: "{{ componentList }}"

【Task1: 新規PARMLIBライブラリー作成】
zos_data_setモジュールを使用し、新たなPARMLIBライブラリー(ここではSYS1.PARMLIB.TES01)を作成します。PARMLIBのライブラリはPDSEだと読み込めないのでPDSにする必要がありますのでご注意ください。

【Task2: 変更対象メンバーのコピー】
zos_copyモジュールを使用し、新しく作成したPARMLIBライブラリー(SYS1.PARMLIB.TEST01)に変更対象のメンバー(IEASYM00)をコピーします。

【Task3: LOADxx変更】
zos_blockinfileモジュールを使用して、SYS0.IPLPARM(LOADK2)にTask1で作成したライブラリーを上位コンカチします。
ここで使用しているzos_blockinfileモジュールを用いることで、複数行の文字列をPDSメンバーやUSSファイルに挿入することができます。今回は1行分しか挿入していないので後述のzos_lineinfileモジュール(1行単位の文字列を扱うためのモジュール)を使うこともできますが、動作確認の意味もあるためあえて別のモジュールを使用しています。
文字列を挿入する位置は、insertbeforeパラメーターで制御しています。ここで指定した正規表現にマッチする行の直前に文字列が挿入されることになります。ここでは、SYS1.PARMLIBの指定行の直前に挿入されるよう指定しています(上位コンカチ)。
挿入した文字列の前後の行には、markerで指定した文字列が挿入されることになります。デフォルトだと以下のような文字列でくくられた行が挿入されることになります。(この前後のマーカー挿入は抑止できなさそうです。)

# BEGIN ANSIBLE MANAGED BLOCK
...
# END ANSIBLE MANAGED BLOCK

参考: Syntax rules for LOADxx
上のマニュアルに記載があるように、LOADxxのコメントは先頭に「*」を指定することになりますので、markerオプションで「*」を先頭に付与した文字列を指定しておきます。
また、この時変更対象のメンバーのオリジナルのバックアップを取得するために、backup: trueを指定しbackup_nameにバックアップ用のメンバー名を指定しています。こうすることで、最悪元のLOADxxを使用してIPLできる状態を残しています。

【Task4: IEASYMxx変更】
zos_lineinfileモジュールを使用して、Task2でコピーしたIEASYM00(LOADxxで上位コンカチされたライブラリーのメンバー)を変更していきます。
このモジュールでは、特定の条件にマッチする行を指定の文字列に置き換えることができます。条件指定には正規表現が利用できます。具体的にはregexpで指定した条件に合致する行がlineで指定した文字列に置き換えられます。
上の例では、「SYMDEF(xxx=」という文字列に合致するものを置き換えるようにしており、xxx部分は変数で与えるようにしています。また、複数のシンボルを同じルールで変更することを想定しているので、Playbookの基本機能であるloop機能を使ってリスト化された変数それぞれに対して置換を実行するようにしています(loopに指定した変数のリストの各項目は {{ item }}変数で参照できます)。
こうすることで変数リスト(上の例だとcomponentList)に指定したシンボルの定義部分を置換することになります。上のPlaybookでは、CICS、MQ、z/OS Connect関連のアドレススペースはIPL時に起動しないよう'NOS'にシンボルの値を変更するようにしています。

Playbook実行

上のPlaybook実行してみます。

実行例
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_ansible_zos_sample_test$ ansible-playbook sample/zos_parmlib_test01.yml 
Using /home/user01/Ansible/VSCode_workspace/my_ansible_zos_sample_test/ansible.cfg as config file

PLAY [modify parmlib] *********************************************************************************************************************************************************

TASK [Create new Parmlib dataset] *********************************************************************************************************************************************
changed: [ezdwazi04] => {
    "changed": true,
    "message": "",
    "names": [
        "SYS1.PARMLIB.TEST01"
    ]
}

TASK [copy parmlib dataset] ***************************************************************************************************************************************************
changed: [ezdwazi04] => {
    "changed": true,
    "dest": "SYS1.PARMLIB.TEST01(IEASYM00)",
    "is_binary": false,
    "src": "SYS1.PARMLIB(IEASYM00)"
}

TASK [add PARMLIB library to LOADxx] ******************************************************************************************************************************************
changed: [ezdwazi04] => {
    "backup_name": "SYS0.IPLPARM(LOADK9)",
    "changed": 1,
    "cmd": "dmod -b -c IBM-1047 -m BEGIN\nEND\n* {mark} ANSIBLE MANAGED BLOCK -j -s -e /^.*PARMLIB  SYS1.PARMLIB[^.]/i\\PARMLIB  SYS1.PARMLIB.TEST01/$ -e $ a\\PARMLIB  SYS1.PARMLIB.TEST01 SYS0.IPLPARM(LOADK2) ",
    "found": 1
}

TASK [replace IPL option] *****************************************************************************************************************************************************
changed: [ezdwazi04] => (item=&CICSTS61) => {
    "ansible_loop_var": "item",
    "changed": 1,
    "cmd": "dsedhelper -d -f -c IBM-1047 -r /^SYMDEF[(]&CICSTS61=/c\\SYMDEF(&CICSTS61='NOS')/$ SYS1.PARMLIB.TEST01(IEASYM00) ",
    "found": 1,
    "item": "&CICSTS61"
}
changed: [ezdwazi04] => (item=&CSQ9MSTR) => {
    "ansible_loop_var": "item",
    "changed": 1,
    "cmd": "dsedhelper -d -f -c IBM-1047 -r /^SYMDEF[(]&CSQ9MSTR=/c\\SYMDEF(&CSQ9MSTR='NOS')/$ SYS1.PARMLIB.TEST01(IEASYM00) ",
    "found": 1,
    "item": "&CSQ9MSTR"
}
changed: [ezdwazi04] => (item=&CSQ9CHIN) => {
    "ansible_loop_var": "item",
    "changed": 1,
    "cmd": "dsedhelper -d -f -c IBM-1047 -r /^SYMDEF[(]&CSQ9CHIN=/c\\SYMDEF(&CSQ9CHIN='NOS')/$ SYS1.PARMLIB.TEST01(IEASYM00) ",
    "found": 1,
    "item": "&CSQ9CHIN"
}
changed: [ezdwazi04] => (item=&ZOSCSRV) => {
    "ansible_loop_var": "item",
    "changed": 1,
    "cmd": "dsedhelper -d -f -c IBM-1047 -r /^SYMDEF[(]&ZOSCSRV=/c\\SYMDEF(&ZOSCSRV='NOS')/$ SYS1.PARMLIB.TEST01(IEASYM00) ",
    "found": 1,
    "item": "&ZOSCSRV"
}

PLAY RECAP ********************************************************************************************************************************************************************
ezdwazi04                  : ok=4    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  

【結果確認】

SYS1.PARMLIB.TEST01という新しいデータセットが作成されています。

image.png

SYS1.PARMLIB.TEST01(IEASYM00) の中身を確認すると、Playbookで指定したシンボルの値が'NOS'に変更されていることが確認できます。
image.png

SYS0.IPLPARM(LOADK2)を確認すると、SYS.PARMLIBの上位にSYS1.PARMLIB.TEST01がコンカチされていることが確認できます。
image.png

SYS0.IPLPARM(LOADK9)を確認すると、元のLOADxx(LOADK2)のバックアップが取得されていることが確認できます。
image.png

変更が問題なさそうなのでIPLして動作確認 => IPL後指定したサブシステムが起動していないことが確認できました。

5.zFSマウント制御

zos_mount というモジュールを使うと、zFSのマウント/アンマウントの制御が行えます。

※このモジュールではマウント/アンマウント処理を行うだけでなく、このマウント設定をIPLをまたがっても永続的にシステムに反映するためにBPXPRMxxにマウントコマンドを仕込む操作も含めて実施する機能が提供されているようです(persistentオプション)。しかしこれを指定するとうまく動かなかったので、ここではその部分は別のモジュールを使用して実装してみます。その方が細かい制御がしやすいかも。

zFSのデータセット、マウントポイントのUSS上のディレクトリは作成済みの想定です。

zFSマウント

zFSをマウントし、BPXPRMxxにマウント用の設定を追加するPlaybookを作成してみます。

zos_mount_zfs.yml
- name: mount zfs
  hosts: ezdwazi04 
  environment: "{{ environment_vars }}"
  vars:
    zfs_dataset: "TEST01.SAMPLE.ZFS"
    zfs_mountpoint: "/tmp/test01"
    bpxprm_member: "SYS1.PARMLIB(BPXPRM01)"
    bpxprm_backup_member: "SYS1.PARMLIB(BPXPRM09)"
  
  tasks:
    - name: mount zfs
      ibm.ibm_zos_core.zos_mount:
        src: "{{ zfs_dataset }}"
        path: "{{ zfs_mountpoint }}"
        fs_type: ZFS
        mount_opts: RW
        state: mounted
      register: tmp_result

    - name: add mount command to BPXPRMxx
      ibm.ibm_zos_core.zos_blockinfile:
        src: "{{ bpxprm_member }}"
        state: present
        marker: "/* {mark} ANSIBLE MANAGED BLOCK for {{ zfs_dataset }} */"
        block: |
          MOUNT FILESYSTEM('{{ zfs_dataset }}')             
            MOUNTPOINT('{{ zfs_mountpoint }}')        
            TYPE(ZFS) MODE(RDWR)    
        backup: true
        backup_name: "{{  bpxprm_backup_member }}"     
      when: tmp_result.changed           

【Task1: マウント】
zFSのデータセットとマウントポイントを指定し、stateオプションにmountedを指定することでマウント処理を行うことになります。マウント時のオプションとしてmount_optsRW (Read/Write)を指定しています。

【Task2: BPXPRMxxへの定義追加】
Task1でマウント処理が行われた場合(changedのステータスになった場合)、BPXPRMxxにMOUNT用のコマンド追記するためのタスクが実行されるように whenオプションを指定しています。
BPXPRMxxへの追記は、zos_blockinfileモジュールを使用しています。

上のPlaybookを実行してみます。
※BPXPRMxxが含まれるデータセットをTSOなどで掴んでいるとBPXPRMxxの変更のタスクがエラーとなりますのでご注意ください。

実行例
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_ansible_zos_sample_test$ ansible-playbook sample/zos_mount_zfs.yml 
Using /home/user01/Ansible/VSCode_workspace/my_ansible_zos_sample_test/ansible.cfg as config file

PLAY [mount zfs] *******************************************************************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************************************
ok: [ezdwazi04]

TASK [mount zfs] *******************************************************************************************************************************************************************
changed: [ezdwazi04] => {
    "allow_uid": true,
    "automove": "AUTOMOVE",
    "automove_list": "",
    "changed": true,
    "cmd": "tsocmd MOUNT FILESYSTEM\\( \\'TEST01.SAMPLE.ZFS\\' \\) MOUNTPOINT\\( \\'/tmp/test01\\' \\) TYPE\\( 'ZFS' \\) MODE\\(RDWR\\) SETUID WAIT SECURITY AUTOMOVE",
    "fs_type": "ZFS",
    "gid": 0,
    "group": "SYS1",
    "mode": "0755",
    "mount_opts": "RW",
    "owner": "BPXROOT",
    "path": "/tmp/test01",
    "persistent": null,
    "rc": 0,
    "size": 0,
    "src": "TEST01.SAMPLE.ZFS",
    "src_params": "",
    "state": "directory",
    "sysname": "",
    "tag_ccsid": null,
    "tag_untagged": null,
    "uid": 0,
    "unmount_opts": "NORMAL"
}

STDOUT:

MOUNT FILESYSTEM( 'TEST01.SAMPLE.ZFS' ) MOUNTPOINT( '/tmp/test01' ) TYPE( ZFS ) MODE(RDWR) SETUID WAIT SECURITY AUTOMOVE


TASK [add mount command to BPXPRMxx] ***********************************************************************************************************************************************
changed: [ezdwazi04] => {
    "backup_name": "SYS1.PARMLIB(BPXPRM09)",
    "changed": 1,
    "cmd": "dmod -b -c IBM-1047 -m BEGIN\nEND\n/* {mark} ANSIBLE MANAGED BLOCK for TEST01.SAMPLE.ZFS */ -j $ a\\MOUNT FILESYSTEM('TEST01.SAMPLE.ZFS')             \n  MOUNTPOINT('/tmp/test01')        \n  TYPE(ZFS) MODE(RDWR)     SYS1.PARMLIB(BPXPRM01) ",
    "found": 0
}

PLAY RECAP *************************************************************************************************************************************************************************
ezdwazi04                  : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

【確認】

SDSFのFSにてZFSがマウントされていることを確認

SDSF FILE SYSTEMS  VS01     VS01                       LINE 1-20 (41)                                                      
COMMAND INPUT ===>                                            SCROLL ===> CSR                                              
NP   DEVICE Path                                 Type     Mode Owner    Name                                         Status
     12     /dsfs                                DSFS     RDWR VS01     OMVS.VS01.UTILITY.DSFS                       ACTIVE
     32     /u                                   AUTOMNT  RDWR VS01     *AMD/u                                       ACTIVE
     48     /tmp/test01                          ZFS      RDWR VS01     TEST01.SAMPLE.ZFS                            ACTIVE
..

BPXPRMxx確認

SYS1.PARMLIB(BPXPRM01)
...
/* BEGIN ANSIBLE MANAGED BLOCK for TEST01.SAMPLE.ZFS */   
MOUNT FILESYSTEM('TEST01.SAMPLE.ZFS')                     
  MOUNTPOINT('/tmp/test01')                               
  TYPE(ZFS) MODE(RDWR)                                    
/* END ANSIBLE MANAGED BLOCK for TEST01.SAMPLE.ZFS */     

末尾に上の設定が追記されていることが確認できました。

zFSアンマウント

同様にアンマウント用のPlaybookを作成してみます。

zos_unmount_zfs.yml
- name: unmount zfs
  hosts: ezdwazi04 
  environment: "{{ environment_vars }}"
  vars:
    zfs_dataset: "TEST01.SAMPLE.ZFS"
    zfs_mountpoint: "/tmp/test01"
    bpxprm_member: "SYS1.PARMLIB(BPXPRM01)"
    bpxprm_backup_member: "SYS1.PARMLIB(BPXPRM09)"

  tasks:
    - name: unmount zfs
      ibm.ibm_zos_core.zos_mount:
        src: "{{ zfs_dataset }}"
        path: "{{ zfs_mountpoint }}"
        fs_type: ZFS
        mount_opts: RW
        state: unmounted
      register: tmp_result

    - name: delete mount command from BPXPRMxx
      ibm.ibm_zos_core.zos_blockinfile:
        src: "{{ bpxprm_member }}"
        state: absent
        marker: "/* {mark} ANSIBLE MANAGED BLOCK for {{ zfs_dataset }} */"
        backup: true
        backup_name: "{{  bpxprm_backup_member }}"   
      when: tmp_result.changed 

【Task1: アンマウント】
マウント用のタスクとほぼ同様ですが、stateパラメーターにunmountedを指定しています。これにより指定したzFSがアンマウントされます。

【Task2: BPXPRMxxから定義削除】
これもマウント時のBPXPRMxxへの追加のTASKとほぼ同様ですが、stateパラメーターにabsentを指定しています。これにより、先に作成したBlockが削除されます。
whenパラメーターでアンマウント処理が行われた場合にのrみ当タスクが実行されるよう指定しています。

上のPlaybookを実行してみます。
※BPXPRMxxが含まれるデータセットをTSOなどで掴んでいるとBPXPRMxxの変更のタスクがエラーとなりますのでご注意ください。

実行例
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_ansible_zos_sample_test$ ansible-playbook sample/zos_unmount_zfs.yml 
Using /home/user01/Ansible/VSCode_workspace/my_ansible_zos_sample_test/ansible.cfg as config file

PLAY [unmount zfs] *****************************************************************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************************************
ok: [ezdwazi04]

TASK [unmount zfs] *****************************************************************************************************************************************************************
changed: [ezdwazi04] => {
    "allow_uid": true,
    "automove": "AUTOMOVE",
    "automove_list": "",
    "changed": true,
    "cmd": "",
    "fs_type": "ZFS",
    "gid": 0,
    "group": "SYS1",
    "mode": "0755",
    "mount_opts": "RW",
    "owner": "BPXROOT",
    "path": "/tmp/test01",
    "persistent": null,
    "rc": 0,
    "size": 4096,
    "src": "TEST01.SAMPLE.ZFS",
    "src_params": "",
    "state": "directory",
    "sysname": "",
    "tag_ccsid": null,
    "tag_untagged": null,
    "uid": 0,
    "unmount_opts": "NORMAL"
}

STDOUT:

UNMOUNT FILESYSTEM( TEST01.SAMPLE.ZFS )


TASK [delete mount command from BPXPRMxx] ******************************************************************************************************************************************
changed: [ezdwazi04] => {
    "backup_name": "SYS1.PARMLIB(BPXPRM09)",
    "changed": 1,
    "cmd": "dmod -b -c IBM-1047 -m BEGIN\nEND\n/* {mark} ANSIBLE MANAGED BLOCK for TEST01.SAMPLE.ZFS */ -j //d SYS1.PARMLIB(BPXPRM01) ",
    "found": 1
}

PLAY RECAP *************************************************************************************************************************************************************************
ezdwazi04                  : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

アンマウント処理が行われ、BPXPRMxxからも先に追加されたMOUNTの定義が削除されます。

6.USS操作

USSファイルのダウンロード/アップロード

ローカル - USS間での単純なファイルのダウンロード/アップロードは、それぞれ zos_fetchモジュール/zos_copyモジュールを使用して実装できます。
これらは、上の「データセットのダンプ/リストア」の例の中で示していますのでそちらをご参照ください。

ディレクトリ単位のアップロード

zos_copyモジュールローカル(Ansible Control Node)にあるディレクトリ配下のファイルを、USS上の特定のディレクトリにアップロードすることができます。元となるディレクトリにサブディレクトリが含まれる場合そのサブディレクトリの構造も含めてUSS上に転送されます。各ファイルは一括で文字コード変換されるか、もしくはバイナリーモードで転送されます。従って対象のディレクトリに含まれるファイルは全て同じencodingが使用されているテキスト・ファイルであるか、もしくは、全てバイナリー(文字コード変換無し)である必要があります。

ローカルに、以下のようなサンプルとして構造のディレクトリを用意します。

user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_ansible_zos_sample_test$ ls -laR sample/files/TEST01
sample/files/TEST01:
total 16
drwxr-xr-x 4 user01 user01 4096 Apr 28 12:59 .
drwxr-xr-x 3 user01 user01 4096 Apr 28 13:01 ..
drwxr-xr-x 2 user01 user01 4096 Apr 28 12:59 subdir01
drwxr-xr-x 2 user01 user01 4096 Apr 28 13:00 subdir02

sample/files/TEST01/subdir01:
total 16
drwxr-xr-x 2 user01 user01 4096 Apr 28 12:59 .
drwxr-xr-x 4 user01 user01 4096 Apr 28 12:59 ..
-rw-r--r-- 1 user01 user01    4 Apr 28 12:59 file01_01.txt
-rw-r--r-- 1 user01 user01    4 Apr 28 12:59 file01_02.txt

sample/files/TEST01/subdir02:
total 16
drwxr-xr-x 2 user01 user01 4096 Apr 28 13:00 .
drwxr-xr-x 4 user01 user01 4096 Apr 28 12:59 ..
-rw-r--r-- 1 user01 user01    4 Apr 28 12:59 file02_01.txt
-rw-r--r-- 1 user01 user01    4 Apr 28 13:00 file02_02.txt

このディレクトリ/ファイル構造をUSS上にcopyするPlaybookを作成してみます。

zos_upload_dir.yml
- name: upload dir to USS
  hosts: ezdwazi04 
  environment: "{{ environment_vars }}"
  vars:
    local_directory: "{{ playbook_dir }}/files/TEST01"
    target_directory: "/u/TEST"    
  gather_facts: no
  
  tasks:
        
    - name: upload dir
      ibm.ibm_zos_core.zos_copy:
        src: "{{ local_directory }}"
        dest: "{{ target_directory }}"
        force: true

上のPlaybookを実行してみます。

実行例
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_ansible_zos_sample_test$ ansible-playbook sample/zos_upload_dir.yml 
Using /home/user01/Ansible/VSCode_workspace/my_ansible_zos_sample_test/ansible.cfg as config file

PLAY [upload dir to USS] ***********************************************************************************************************************************************************

TASK [upload dir] ******************************************************************************************************************************************************************
changed: [ezdwazi04] => {
    "changed": true,
    "dest": "/u/TEST/TEST01",
    "gid": 0,
    "group": "SYS1",
    "is_binary": false,
    "mode": "0755",
    "owner": "BPXROOT",
    "size": 8192,
    "src": "/home/user01/Ansible/VSCode_workspace/my_ansible_zos_sample_test/sample/files/TEST01",
    "state": "directory",
    "uid": 0
}

PLAY RECAP *************************************************************************************************************************************************************************
ezdwazi04                  : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

USS上のディレクトリを確認してみます。

IBMUSER : /u/TEST : > ls -laRT TEST01

TEST01:
total 64
                    drwxr-xr-x   4 BPXROOT  SYS1        8192 Apr 28 00:17 .
                    drwxr-xr-x   4 BPXROOT  SYS1        8192 Apr 28 00:17 ..
                    drwxr-xr-x   2 BPXROOT  SYS1        8192 Apr 28 00:17 subdir01
                    drwxr-xr-x   2 BPXROOT  SYS1        8192 Apr 28 00:17 subdir02

TEST01/subdir01:
total 36
                    drwxr-xr-x   2 BPXROOT  SYS1        8192 Apr 28 00:17 .
                    drwxr-xr-x   4 BPXROOT  SYS1        8192 Apr 28 00:17 ..
t IBM-1047    T=on  -rw-r--r--   1 BPXROOT  SYS1           4 Apr 28 00:17 file01_01.txt
t IBM-1047    T=on  -rw-r--r--   1 BPXROOT  SYS1           4 Apr 28 00:17 file01_02.txt

TEST01/subdir02:
total 36
                    drwxr-xr-x   2 BPXROOT  SYS1        8192 Apr 28 00:17 .
                    drwxr-xr-x   4 BPXROOT  SYS1        8192 Apr 28 00:17 ..
t IBM-1047    T=on  -rw-r--r--   1 BPXROOT  SYS1           4 Apr 28 00:17 file02_01.txt
t IBM-1047    T=on  -rw-r--r--   1 BPXROOT  SYS1           4 Apr 28 00:17 file02_02.txt

ディレクトリ構造を含み、各ファイルが転送されていることがわかります。各ファイルはデフォルトではIBM-1047の文字コードに変換されています。

ちなみに、この後USS上のsubdir01配下のファイルを以下のように変更してみました。

  • file01_01.txtを削除
  • file01_02.txtの中身を更新
  • file01_03.txtという新規ファイルを作成

その後、再度上のPlaybookを実行してみました。その結果、file01_01.txt, file01_02.txtはローカルの情報に上書きされましたが、新規で作成されたfile01_03.txtはそのまま残った状態となっていました。

zos_copyモジュールでは、上のようにディレクトリ構造をまるごとアップロードすることができましたが、zos_fetchモジュールではUSSディレクトリ配下をまごとダウンロードするという操作はできないようです。

USSディレクトリの圧縮/解凍

USSディレクトリやファイルを圧縮/解凍する際は、それぞれzos_archiveモジュール/zos_unarchiveモジュールが利用できます。
(※これらのモジュールはUSS上のディレクトリ/ファイル以外にMVSデータセットの圧縮/解凍にも利用できます)

ここでは、ある環境で適当なUSSディレクトリをtarで固めて取得し、別の環境で解凍する、という操作を試してみたいと思います。

tarファイルへの圧縮、ローカルへの保管

あるディレクトリをtarで固めてローカル(Ansible Control Node)に取得するPlaybookを作成してみます。

zos_archive.yml
- name: archive USS directory
  hosts: ezdwazi04 
  environment: "{{ environment_vars }}"
  vars:
    source_directory: "/u/ibmuser/.ssh"
    tmp_USS_directory: "/tmp"
    backup_file: "ibmuser_ssh.tar"
    local_directory: "{{ playbook_dir }}/backup"
  gather_facts: no
  
  tasks:
        
    - name: archive
      ibm.ibm_zos_core.zos_archive:
        src: "{{ source_directory }}"
        dest: "{{ tmp_USS_directory }}/{{ backup_file }}"
        format:
          name: tar     
        force: true

    - name: download
      ibm.ibm_zos_core.zos_fetch:
        src: "{{ tmp_USS_directory }}/{{ backup_file }}"
        dest: "{{ local_directory }}/{{ backup_file }}"
        is_binary: true
        flat: true

上のPlaybookを実行してみます。

実行例
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_ansible_zos_sample_test$ ansible-playbook sample/zos_archive.yml 
Using /home/user01/Ansible/VSCode_workspace/my_ansible_zos_sample_test/ansible.cfg as config file

PLAY [archive USS directory] *******************************************************************************************************************************************************

TASK [archive] *********************************************************************************************************************************************************************
changed: [ezdwazi04] => {
    "archived": [
        "/u/TEST/TEST01/subdir01",
        "/u/TEST/TEST01/subdir02",
        "/u/TEST/TEST01/subdir01/file01_02.txt",
        "/u/TEST/TEST01/subdir01/file01_01.txt",
        "/u/TEST/TEST01/subdir02/file02_01.txt",
        "/u/TEST/TEST01/subdir02/file02_02.txt"
    ],
    "arcroot": "/u/TEST",
    "changed": true,
    "dest": "/tmp/TEST01.tar",
    "dest_state": "archive",
    "expanded_exclude_sources": [],
    "expanded_sources": [
        "/u/TEST/TEST01"
    ],
    "gid": 0,
    "group": "SYS1",
    "missing": [],
    "mode": "0644",
    "owner": "BPXROOT",
    "size": 20480,
    "state": "file",
    "uid": 0,
    "xmit_log_data_set": ""
}

TASK [download] ********************************************************************************************************************************************************************
changed: [ezdwazi04] => {
    "changed": true,
    "data_set_type": "USS",
    "dest": "/home/user01/Ansible/VSCode_workspace/my_ansible_zos_sample_test/sample/backup/TEST01.tar",
    "file": "/tmp/TEST01.tar",
    "is_binary": true
}

PLAY RECAP *************************************************************************************************************************************************************************
ezdwazi04                  : ok=2    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

指定したディレクトリが/tmp/TEST01.tarに固められ、それがローカルの.../backup/TEST01.tarファイルにダウンロードされました。

tarファイルの転送、解凍

上で取得されたtarファイルを、USS上の特定のディレクトリに解凍するPlaybookを作成してみます。

zos_unarchive.yml
- name: unarchive USS directory
  hosts: ezdwazi04 
  environment: "{{ environment_vars }}"
  vars:
    local_directory: "{{ playbook_dir }}/backup"
    backup_file: "TEST01.tar"
    target_directory: "/u/XXX"
  gather_facts: no
  
  tasks:

    - name: unarchive
      ibm.ibm_zos_core.zos_unarchive:
        src: "{{ local_directory }}/{{ backup_file }}"  
        dest: "{{ target_directory }}"
        format:
          name: tar
        force: true

zos_unarchiveモジュールでは、ファイル転送と解凍を1つのタスクで実行できるようです。

上のPlaybookを実行してみます。

実行例
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_ansible_zos_sample_test$ ansible-playbook sample/zos_unarchive.yml 
Using /home/user01/Ansible/VSCode_workspace/my_ansible_zos_sample_test/ansible.cfg as config file

PLAY [unarchive USS directory] *****************************************************************************************************************************************************

TASK [unarchive] *******************************************************************************************************************************************************************
changed: [ezdwazi04] => {
    "changed": true,
    "dest": "/tmp/ansible.y2b1r7ro",
    "dest_path": "/u/XXX",
    "gid": 0,
    "group": "SYS1",
    "is_binary": true,
    "missing": [],
    "mode": "0600",
    "owner": "BPXROOT",
    "size": 20480,
    "src": "/tmp/ansible.y2b1r7ro",
    "state": "file",
    "targets": [
        "TEST01/subdir01",
        "TEST01/subdir01/file01_01.txt",
        "TEST01/subdir01/file01_02.txt",
        "TEST01/subdir02",
        "TEST01/subdir02/file02_01.txt",
        "TEST01/subdir02/file02_02.txt",
        "TEST01/subdir01/file01_02.txt",
        "TEST01/subdir01/file01_01.txt",
        "TEST01/subdir02/file02_01.txt",
        "TEST01/subdir02/file02_02.txt"
    ],
    "uid": 0
}

PLAY RECAP *************************************************************************************************************************************************************************
ezdwazi04                  : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

結果確認

IBMUSER : /u/XXX : > ls -laRT TEST01

TEST01:
total 64
                    drwxr-xr-x   4 BPXROOT  SYS1        8192 Apr 28 00:35 .
                    drwxr-xr-x   3 BPXROOT  SYS1        8192 Apr 28 00:35 ..
                    drwxr-xr-x   2 BPXROOT  SYS1        8192 Apr 28 00:35 subdir01
                    drwxr-xr-x   2 BPXROOT  SYS1        8192 Apr 28 00:35 subdir02

TEST01/subdir01:
total 36
                    drwxr-xr-x   2 BPXROOT  SYS1        8192 Apr 28 00:35 .
                    drwxr-xr-x   4 BPXROOT  SYS1        8192 Apr 28 00:35 ..
t IBM-1047    T=on  -rw-r--r--   1 BPXROOT  SYS1           4 Apr 28 00:17 file01_01.txt
t IBM-1047    T=on  -rw-r--r--   1 BPXROOT  SYS1           4 Apr 28 00:17 file01_02.txt

TEST01/subdir02:
total 36
                    drwxr-xr-x   2 BPXROOT  SYS1        8192 Apr 28 00:35 .
                    drwxr-xr-x   4 BPXROOT  SYS1        8192 Apr 28 00:35 ..
t IBM-1047    T=on  -rw-r--r--   1 BPXROOT  SYS1           4 Apr 28 00:17 file02_01.txt
t IBM-1047    T=on  -rw-r--r--   1 BPXROOT  SYS1           4 Apr 28 00:17 file02_02.txt

先にtarで固められたディレクトリがターゲットのディレクトリ先に展開されました。

USS上でのスクリプト実行

zos_scriptモジュールを使用すると、USS上でスクリプトを実行することができます。
ここでは、Jinja2テンプレートを使用したシェル・スクリプトの雛形を用意してそれを実行するPlaybookを作成してみます。

まずは実行したいスクリプトのJinja2テンプレートを用意します。ここではテスト用に単純なディレクトリを作成するだけのシェル・スクリプトを作成します。

create_dir.sh
#!/bin/sh

mkdir -p {{ new_dir }}

作成するディレクトリは変数化しています。

これを実行するためのPlaybookを作成します。

zos_script.yml
- name: execute USS script
  hosts: ezdwazi04 
  environment: "{{ environment_vars }}"
  vars:
    new_dir: "/tmp/AAA"
  gather_facts: no
  
  tasks:

    - name: execute script
      ibm.ibm_zos_core.zos_script:
        cmd: "{{ playbook_dir }}/templates/create_dir.sh"
        use_template: true

上のPLaybookを実行してみます。

実行例
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_ansible_zos_sample_test$ ansible-playbook sample/zos_script.yml 
Using /home/user01/Ansible/VSCode_workspace/my_ansible_zos_sample_test/ansible.cfg as config file

PLAY [execute USS script] **********************************************************************************************************************************************************

TASK [execute script] **************************************************************************************************************************************************************
changed: [ezdwazi04] => {
    "changed": true,
    "cmd": "/home/user01/Ansible/VSCode_workspace/my_ansible_zos_sample_test/sample/template/create_dir.sh",
    "rc": 0,
    "remote_cmd": "/u/ibmuser/.ansible/tmp/zos_script.dve7cwhv.create_dir.sh",
    "tempfile_path": "/u/ibmuser/.ansible/tmp/zos_script.dve7cwhv.create_dir.sh"
}

PLAY RECAP *************************************************************************************************************************************************************************
ezdwazi04                  : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

結果確認

IBMUSER : /SYSTEM/tmp : > cd /
IBMUSER : / : > cd /tmp
IBMUSER : /SYSTEM/tmp : > ls -la | grep AAA
drwxr-xr-x   2 BPXROOT  SYS1        4096 Apr 28 04:18 AAA

USS上に指定したディレクトリが作成されていることが確認できました。

7.WTORへのReply

zos_operator_action_queryモジュールを使うと、WTORのメッセージをハンドリングすることができます。

例えばSYSLOGに以下のようにWTORのメッセージが出ている状況を想定します。

image.png

ここで、WTORメッセージのReply Numberを判別してIMSに対してReplyによるコマンド投入を行うPlaybookを作成してみます。

zos_operator_action_uery.yml
- name: Reply to WTOR 
  hosts: ezdwazi04 
  environment: "{{ environment_vars }}"
  vars:
    target_job_name: "IMS15CR1"
    target_message_id: "DFS996I"
    #reply_command: "/DIS,ACT"
    reply_command: "/DIS,STATUS,DATABASE"

  gather_facts: no
  
  tasks:
    - name: get all WTOR messages
      ibm.ibm_zos_core.zos_operator_action_query:
      register: tmp_result_all

    - name: get WTOR messages for IMS READY (DFH996I)
      ibm.ibm_zos_core.zos_operator_action_query:
        job_name: "{{ target_job_name }}"
        message_id: "{{ target_message_id }}"
      register: tmp_result
    
    - name: debug
      debug:
        var: tmp_result.actions[0].number

    - name: reply 
      ibm.ibm_zos_core.zos_operator:
        cmd: "R {{ tmp_result.actions[0].number }},{{ reply_command }}"
      register: tmp_result
      changed_when: false

【Task1: get all WTOR messages】
動作確認のため、特にフィルターをかけずに全てのWTORメッセージを取得します。

【Task2: get WTOR messages for IMS READY (DFH996I) 】
job name, message idを明示指定して特定のWTORメッセージを取得します。

【Task3: debug】
デバッグ用に上で取得したWTORメッセージのReply番号を表示させます。

【Task4: reply】
上で取得したReply番号に対してReplyによりコマンドを投入します。ここではIMSの/DISPLAYコマンドを投入します。

上のPlaybookを実行してみます。

実行例
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_ansible_zos_sample_test$ ansible-playbook sample/zos_operator_action_query.yml 
Using /home/user01/Ansible/VSCode_workspace/my_ansible_zos_sample_test/ansible.cfg as config file

PLAY [Reply to WTOR] ***************************************************************************************************************************************************************

TASK [get all WTOR messages] *******************************************************************************************************************************************************
ok: [ezdwazi04] => {
    "actions": [
        {
            "job_id": "STC00045",
            "job_name": "IMS15CR1",
            "message_id": "DFS996I",
            "message_text": "DFS996I *IMS READY*  IVP1",
            "number": "0025",
            "system": "VS01",
            "type": "R"
        },
        {
            "job_name": "CNMPROC",
            "message_id": "DSI802A",
            "message_text": "DSI802A CNM01    REPLY WITH VALID",
            "number": "0002",
            "system": "VS01",
            "type": "R"
        }
    ],
    "changed": false,
    "count": 2
}

TASK [get WTOR messages for IMS READY (DFH996I)] ***********************************************************************************************************************************
ok: [ezdwazi04] => {
    "actions": [
        {
            "job_id": "STC00045",
            "job_name": "IMS15CR1",
            "message_id": "DFS996I",
            "message_text": "DFS996I *IMS READY*  IVP1",
            "number": "0025",
            "system": "VS01",
            "type": "R"
        }
    ],
    "changed": false,
    "count": 1
}

TASK [debug] ***********************************************************************************************************************************************************************
ok: [ezdwazi04] => {
    "tmp_result.actions[0].number": "0025"
}

TASK [reply] ***********************************************************************************************************************************************************************
ok: [ezdwazi04] => {
    "changed": false,
    "cmd": "R 0025,/DIS,STATUS,DATABASE",
    "content": [
        "VS01       2024119  05:38:30.00             ISF031I CONSOLE IBMU0000 ACTIVATED",
        "VS01       2024119  05:38:30.00            -R 0025,/DIS,STATUS,DATABASE ",
        "VS01       2024119  05:38:30.00             IEE600I REPLY TO 0025 IS;/DIS,STATUS,DATABASE"
    ],
    "elapsed": 1.02,
    "rc": 0,
    "wait_time_s": 1
}

PLAY RECAP *************************************************************************************************************************************************************************
ezdwazi04                  : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

SYSLOG確認
image.png

DFS996I(IMS Ready)のWTORに対してReplyによりコマンド投入されたことが確認できました。

※今回はReplyの動作確認のために上のようなPlaybookでIMSに対してReply実行してみましたが、IMS管理用に提供されている IBM z/OS IMS collection提供のモジュールを使用すればもう少しスマートにコマンド投入できそうです。
参考: ims_commandモジュール

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0