Wazi aaS: クラウド上でのメインフレーム開発環境構築 - (7)' Wazi Image Builder - Trouble Shootingメモ

# はじめに
とある環境でWazi Image Builderを使用して既存のオンプレz/OS環境からIBM Cloud環境への移植を行いました。その際に発生した主要な問題とその時の対応方法を参考までに整理しておきます。
※環境依存の状況もあるかと思いますので当記事に記載した対応方法がすべての環境で適用できるとは限りません。あらかじめご了承願います。

## 関連記事

[Wazi aaS: クラウド上でのメインフレーム開発環境構築 - (1) 概要](https://qiita.com/tomotagwork/items/487d91ea8ddfb50e7257)
[Wazi aaS: クラウド上でのメインフレーム開発環境構築 - (2) 仮想サーバー作成](https://qiita.com/tomotagwork/items/9cea4c804f260d609eed)
[Wazi aaS: クラウド上でのメインフレーム開発環境構築 - (3) ネットワーク構成](https://qiita.com/tomotagwork/items/be2803e504b87cf364e5)
[Wazi aaS: クラウド上でのメインフレーム開発環境構築 - (4) Wazi aaS への接続](https://qiita.com/tomotagwork/items/b59717d767a91628b825)
[Wazi aaS: クラウド上でのメインフレーム開発環境構築 - (5) Stock Iamge確認](https://qiita.com/tomotagwork/items/40b7eaec61e31f830cbe)
[Wazi aaS: クラウド上でのメインフレーム開発環境構築 - (6) Stock Iamge基本操作/カスタマイズ](https://qiita.com/tomotagwork/items/0f2e914fd6263f2b40db)
[Wazi aaS: クラウド上でのメインフレーム開発環境構築 - (7) Wazi Image Builder](https://qiita.com/tomotagwork/items/fcfd295aaa54fa597c4b)
[Wazi aaS: クラウド上でのメインフレーム開発環境構築 - (7)' Wazi Image Builder - Trouble Shootingメモ](https://qiita.com/tomotagwork/items/fd804734595644a09d47)
[Wazi aaS: クラウド上でのメインフレーム開発環境構築 - (8) Wazi aaS仮想サーバーの複製](https://qiita.com/tomotagwork/items/ef23025b1ca3800b73c1)
[Wazi aaS: クラウド上でのメインフレーム開発環境構築 - (9) TerraformによるWazi aaS仮想サーバーの管理](https://qiita.com/tomotagwork/items/883652c1534d12f2b042)

# 発生事象/対応措置例

## Case1: RACF DASDVOLクラス権限

### 事象
WIBコンソールから"コンポーネントの作成"を行った際にエラーが発生。
VOLの一覧はWIBコンソール上で表示できてきており、コンポーネントとして抽出したい対象のVOLを選択してコンポーネントの作成を実行すると、指定したVOLUMEの抽出に軒並み失敗。
(VOLの一覧がWIBコンソール上で表示されていたので、Extraction Utilityは動いていると言える。)

### 調査/確認
WIBサーバーの実体であるLibertyのログ(`/opt/ibm/wib/wib-server/logs/messages.log`)を確認すると各VOLの抽出の際にDASDVOLの権限に関してこんな感じのメッセージが出ていた。

```
com.ibm.zdt.handler.PipingDataTransferHander   E [ZDT-LOG-ERROR] Extracting volume VOL001 for component Component[name=z25sys1,version=1,source system=SourceSystem[label=Z25A,hostname=xxx.xxx.xxx.xxx,port=22,type=SYSTEM TYPE Z]]: failed on executing commmand _BPXK_JOBLOG=STDERR /usr/lpp/IBM/zdt/feuc image 'VOL001''/home/zwazit/extraction_pipe.nnnnnnnnnnnnn' compress with return code 10 and error message: 
stdout: HH:MM:SS.SS zD&T data migration   20230718-121810710
 16:28:25.91 Access to DASDVOL VOL001 not granted: RACROUTE RC=4
 ,stderr:<empty>
```
※この時、ソース環境のz/OSのSYSLOGには特にRACF関連のメッセージは出力されていないのが非常に分かりにくい。

z/OS側の状況としては、DASDVOLクラスがアクティベートされていたが、プロファイルが全く存在していない状態だった。

### 参考 
[Wazi aaS - Creating a volume component](https://www.ibm.com/docs/en/wazi-aas/1.0.0?topic=components-creating-volume-component)
>If you create a volume component, grant READ access to the SAF profile in class DASDVOL for the volumes that are extracted to the user ID that is used in the Wazi Image Builder. For more information about DASD volume migration, see section 15.3.2 "RACF® requirements" in the zPDT® Guide and Reference.

[Redbook - ISV IBM zPDT Guide and Reference](https://www.redbooks.ibm.com/abstracts/sg248205.html)

>15.3.2 RACF requirements
The server program requires RACF class DASDVOL to be active and the server program must have at least READ access to all volsers to be transferred. You must determine whether the DASDVOL class is active and used on the system where you install the migration utility server. You can do this task by logging on to TSO with a user ID with RACF SPECIAL authority and issue these TSO commands from a READY prompt or ISPF option 6:
  SETROPTS LIST (Check the list of active classes.)
  RLIST DASDVOL * (See whether any profiles are defined.)
If these checks are negative, you can assume that the DASDVOL class is not being used.

>DASDVOL not in use
The next step is to decide whether only certain volumes should be subject to copying by this migration utility or whether all volumes might be accessed for migration. If you decide to allow migration of all volumes, enter these TSO commands:
  SETROPTS CLASSACT(DASDVOL) (Activate the DASDVOL class.)
  SETROPTS RACLIST(DASDVOL) (Optional, but recommended here.)
  RDEFINE DASDVOL ** UACC(ALTER) (Allow universal alter access.)
  SETROPTS RACLIST(DASDVOL) REFRESH (If you RACLISTed the class.)
The DASDVOL class is used only by a selected group of utility programs, such as dump or restore. Allowing UACC(ALTER) does not open all your data sets to access by all users.
Whatever RACF data set protection that you have in place (through the ADDSD and PERMIT commands) is still effective.
This setup is the extent of the RACF setup if DASDVOL was not initially active and you want to allow the migration server to access any volume. If you want to limit the volumes that are subject to migration, you should work with an experienced RACF administrator. The general
technique is to restrict global access to DASDVOL (perhaps with a UACC(NONE) condition) and then issue PERMIT commands to cover the volumes that you want to migrate. For example:
  PERMIT VOL123 CLASS(DASDVOL) ID(IBMUSER) UACC(READ)
  PERMIT ADCD* CLASS(DASDVOL) ID(IBMUSER) UACC(READ)
  SETROPTS RACLIST(DASDVOL)REFRESH (If you RACLISTed DASDVOL.)

>In this example, volser VOL123 and any volser beginning with Application Development Controlled Distribution (ADCD) can be accessed by the migration utility.
The usage of the DASDVOL class might have side effects on other utility programs. If DASDVOL was not active before your migration activities, you might want to deactivate it when the migration activities are completed:
  SETROPTS NORACLIST(DASDVOL)
  SETROPTS NOCLASSACT(DASDVOL)

>DASDVOL in use
If you find that the DASDVOL class is active on your server system, discuss the situation with the system programmers that manage that system. Your requirements are simple: you (meaning the user ID who runs the migration server program) need DASDVOL with READ access to whatever volsers that you intend to migrate.

### 対応措置

総称プロファイル(**)にALTER権限付与することで解決。

コマンド例:

```
RDEFINE DASDVOL ** UACC(ALTER) AUDIT(ALL(READ))
```

## Case2: IBM Cloudリソース・アクセス権限

### 事象
WIB管理コンソール上で、ターゲット環境の設定を行う際に問題が発生。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/bddd3ffc-fdeb-2642-66b0-e780be93ba2d.png)


ターゲットとなるIBM Cloudの情報を設定する画面で、APIキーを入力して接続をクリック => 接続OK
プルダウンからリソース・グループを選択 => OK
Cloud Object Storageインスタンスを選択するためにプルダウンメニューを表示 => <font color="red">リストが表示されず選択できない!</font>

### 調査/確認
WIBサーバーのLibertyのログを見てもエラーは出力されていなかった。当該APIキーを生成したユーザーでIBM CloudにログインしてICOSインスタンスを確認すると、存在が確認できる状態(リストされる)。
<font color="red">※どこにもエラーメッセージらしきものが表示されないので問題判別が困難!</font>

### 参考
[IBM Cloud - Resource Controller - Get a list of all resource instances](https://cloud.ibm.com/apidocs/resource-controller/resource-controller#list-resource-instances)

恐らく内部的にはリソース・コントローラーに対して`GET /v2/resource_instances`こちらのREST APIを発行してICOSインスタンスのリストを取得しようとしていると思われる。
そのためには、上のリンクに記載されている通り `resource-controller.instance.retrieve` の権限が必要。

### 対応措置

API Keyを発行したユーザーが属するグループのポリシーを編集し、ICOSのサービスに対して"プラットフォーム・アクセス"のビューワー権限を付与した。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/e565311a-986f-4de8-5adb-c8689c3c4de7.png)
上のチェックを付けることで、`resource-controller.instance.retrieve` の権限が付与されることになるものと思われる。

その後リトライしたところ、ICOSインスタンスのリストがプルダウンに表示されるようになった。

## Case3: gzファイル破損

### 事象

WIB管理コンソール上でイメージのデプロイ処理を行った時にエラーで失敗。
terraform apply処理でエラー。
デプロイ処理では、内部的にterraformの機能が使用されIBM Cloud上にデータ加工用の仮想サーバー(Linux)を一時的に作成し、そこでCloud initという仕組みを利用して"data mover"と呼ばれるPython Programを実行しデータの加工を行っている。この処理の途中でエラーが発生した。

### 調査/確認
WIBサーバーのterraformのディレクトリ下(`<wib_install_path>/wib-server/terraform/`)に、一時的に作成された仮想サーバーにSSHでアクセスするためのprivate keyが作成されている(private_key)。これを使用することで、rootユーザーでこの仮想サーバーにアクセスすることが可能。
作成された仮想サーバーの浮動IP(FloatingIP)をIBM Cloudのコンソールから確認し、scpにて仮想サーバー上のCloud init関連のログ・ファイルを取得する。

コマンド例:

```
scp -i <wib_inst_path>/wib-server/terraform/private_key root@<floating ip of the temporary VSI>:/var/log/cloud*.log /tmp
```

<font color="red"> ※terraformのapplyで失敗すると、その後一時的に作成されていた各種IBM Cloud上のリソースがterraform destroyにより破棄されてしまう。破棄される前に上のコマンドを投入しないと資料が取得できないので注意！</font>


仮想サーバー内の`/va/log/cloud-init.log` および `/var/log/cloud-init-output.log` を確認する。



一時的に作成された仮想サーバー内の `/var/log/cloud-init-output.log` を確認すると、以下のようなエラーで"data mover"(Python)が終了していた。

```
...

Fetch VOL006 gzip volume from COS and uncompress to /volumes/data/VOL006
Retrieving item from bucket: icos-bkt, key: VOL006_2024-02-19_22_33_54.gz
Retrieving bucket contents from: icos-bkt
Done fetching VOL001 to /volumes/data/VOL001
Done fetching VOL002 to /volumes/data/VOL002
Fetch VOL007 gzip volume from COS and uncompress to /volumes/data/VOL007
Retrieving item from bucket: icos-bkt, key: VOL007_2024-02-19_22_35_16.gz
Retrieving bucket contents from: icos-bkt
Done fetching VOL004 to /volumes/data/VOL004
Fetch VOL008 gzip volume from COS and uncompress to /volumes/data/VOL008
Retrieving item from bucket: icos-bkt, key: VOL008_2024-02-19_20_44_28.gz
Retrieving bucket contents from: icos-bkt
Done fetching VOL005 to /volumes/data/VOL005
Fetch VOL009 gzip volume from COS and uncompress to /volumes/data/VOL009
Retrieving item from bucket: icos-bkt, key: VOL009_2024-02-19_19_52_57.gz
Retrieving bucket contents from: icos-bkt
Done fetching VOL006 to /volumes/data/VOL006
Fetch VOL010 gzip volume from COS and uncompress to /volumes/data/VOL010
Retrieving item from bucket: icos-bkt, key: VOL010_2024-02-19_20_07_42.gz
Retrieving bucket contents from: icos-bkt
Done fetching VOL007 to /volumes/data/VOL007
Done fetching VOL008 to /volumes/data/VOL008
Done fetching VOL009 to /volumes/data/VOL009
Done fetching VOL010 to /volumes/data/VOL010
multiprocessing.pool.RemoteTraceback: 
"""
Traceback (most recent call last):
  File "/usr/lib/python3.8/multiprocessing/pool.py", line 125, in worker
    result = (True, func(*args, **kwds))
  File "/usr/lib/python3.8/multiprocessing/pool.py", line 48, in mapstar
    return list(map(*args))
  File "./data_mover.py", line 204, in get_volume_file
    shutil.copyfileobj(f_gzip, f_out)
  File "/usr/lib/python3.8/shutil.py", line 205, in copyfileobj
    buf = fsrc_read(length)
  File "/usr/lib/python3.8/gzip.py", line 292, in read
    return self._buffer.read(size)
  File "/usr/lib/python3.8/_compression.py", line 68, in readinto
    data = self.read(len(byte_view))
  File "/usr/lib/python3.8/gzip.py", line 479, in read
    if not self._read_gzip_header():
  File "/usr/lib/python3.8/gzip.py", line 427, in _read_gzip_header
    raise BadGzipFile('Not a gzipped file (%r)' % magic)
gzip.BadGzipFile: Not a gzipped file (b'l\x89')
"""

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "./data_mover.py", line 324, in <module>
    data_volumes = p.map(get_volume_file, volumes)
  File "/usr/lib/python3.8/multiprocessing/pool.py", line 364, in map
    return self._map_async(func, iterable, mapstar, chunksize).get()
  File "/usr/lib/python3.8/multiprocessing/pool.py", line 771, in get
    raise self._value
gzip.BadGzipFile: Not a gzipped file (b'l\x89')
Cloud-init v. 23.3.1-0ubuntu1~20.04.1 running 'modules:final' at Tue, 20 Feb 2024 05:59:46 +0000. Up 18.28 seconds.
2024-02-20 06:22:25,254 - cc_scripts_user.py[WARNING]: Failed to run module scripts_user (scripts in /var/lib/cloud/instance/scripts)
2024-02-20 06:22:25,255 - util.py[WARNING]: Running module scripts_user (<module 'cloudinit.config.cc_scripts_user' from '/usr/lib/python3/dist-packages/cloudinit/config/cc_scripts_user.py'>) failed
Cloud-init v. 23.3.1-0ubuntu1~20.04.1 finished at Tue, 20 Feb 2024 06:22:25 +0000. Datasource DataSourceNoCloud [seed=/dev/vdc][dsmode=net].  Up 1377.36 seconds
```

エラーの出方が恐ろしく不親切。
どうやらz/OSから吸い上げたVolumeのファイル(*.gz)を解凍しようとしたときに、ファイルが壊れていて解凍に失敗したらしい、ということがなんとなく読み取れる。しかし、どのファイルの解凍に失敗したのかがメッセージからすぐに分からない。
前段のメッセージを見ていると、各VOLファイルごとに、以下のメッセージがセットで出ているらしいことが分かる。
```
Fetch VOL010 gzip volume from COS and uncompress to /volumes/data/VOL010
Retrieving item from bucket: icos-bkt, key: VOL010_2024-02-19_20_07_42.gz
Retrieving bucket contents from: icos-bkt
Done fetching VOL010 to /volumes/data/VOL010
```
そこで、以下の2つのメッセージを抜き出して比較し、Fetch開始されているが完了していないVOLを抜き出せば、そのファイルが壊れている可能性が高いというあたりをつけることができる。
`Fetch <VOL> ...`
`Done fetching <VOL> ...`

### 対応措置

上であたりをつけたVOLをz/OSから抽出しなおしてデプロイ処理をリトライすると、エラーは発生せずにデプロイが正常に完了した。
ファイルが壊れた根本原因は不明。Extraction Utilityがgzファイルをうまく作成できなかったか、z/OS => sftpサーバー => ICOSの転送の際にファイルが壊れたか...。
同じことを繰り返せばうまく動くというのはあまりうれしい話では無いのだが。まぁレアケースを踏んだと思うことにしよう。



## Case4: 通信装置アドレス数の制約

### 事象/調査/確認

イメージのデプロイ時にICOS上に生成されたdevmapファイルの中身を確認すると、OSA関連のデバイスの定義に不備があることが確認された。

```
# This file was generated at 20230529_014645 -0400

[system]
processors	0
memory	4000m
ipl	0A80	"0A82K2"
3270port	3270
system_name	DEFSYS

[manager]
name	aws3274	0001	#define a few 3270 terminals 
device	0700	3279	3274	tso
device	0701	3279	3274	tso
device	0702	3279	3274	tso
device	0703	3279	3274	tso
device	0704	3279	3274	tso

[manager]
name	awsosa	0002	--path=A0	--pathtype=OSD	--tunnel_intf=y	--tunnel_ip=172.26.1.1	#QDIO mode 
device	0400	osa	osa	--unitadd=0
device	0401	osa	osa	--unitadd=1
device	0402	osa	osa	--unitadd=2
device	0403	osa	osa	--unitadd=3
device	0404	osa	osa	--unitadd=4
device	0405	osa	osa	--unitadd=5
device	0406	osa	osa	--unitadd=6
device	0407	osa	osa	--unitadd=7
device	0408	osa	osa	--unitadd=8
device	0409	osa	osa	--unitadd=9
#device	040A	osa	osa	--unitadd=A
#device	040B	osa	osa	--unitadd=B
#device	040C	osa	osa	--unitadd=C
#...

[manager]
name	awsckd	0003
device	0A89	3390	3390	/volumes/B5USR1
device	0A99	3390	3390	/volumes/B5PAGC
device	0A98	3390	3390	/volumes/B5PRD4
device	0A80	3390	3390	/volumes/B5RES1
device	0A8B	3390	3390	/volumes/B5PRD5
...
```

利用予定のOSAPORTで使用するはずのアドレスが、devicemap上ではコメントアウトされていた。
恐らく通信用に使用するOSA用のアドレスは10個までという内部的な制約があるのではないかと思われるが、マニュアルに記載は見当たらない。
事前に発見できたので問題は顕在化しなかったが、恐らくこのまますすめるとネットワークの疎通ができない問題が発生していたのではないかと推察される。
これ、かなり危険なトラップだと思われる...

### 対応措置

WIB管理コンソール上の操作で「[イメージの作成](https://qiita.com/tomotagwork/items/fcfd295aaa54fa597c4b#%E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8%E3%81%AE%E4%BD%9C%E6%88%90)」を行う際にIPL情報をカスタマイズすることができる。ここで、デフォルトではソースのz/OS環境で使用されていた通信装置用のアドレスが全て定義されることになる(おそらくIODFに含まれるOSA用のアドレスが全て含まれると思われる)。これが10以上あると後半がコメントアウトされてしまうと思われるため、使用するアドレス(当該TRL Major NodeのREAD,WRITE,DATAPATHに指定しているアドレス)以外は全て削除しておく必要がある。

この部分
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/8b062d3e-7830-bf8e-a991-ef249961290a.png)


## Case5: IPL失敗/IEA303W ABEND 878 

### 事象
WIBを使用して作成したカスタム・イメージを元にWazi aaS仮想サーバーを作成し、起動しようとしたところ、IPL時に以下のエラーで失敗。

```
IEA303W ABEND 878 reason 00000004 during initialization UNDER RIM IEAVNP23
```

<font color="red">※一度起動できた環境でも、その後IPLし直した時に上の878 ABENDが発生することがあった。そのまま何度か再起動をリトライしているとIPLできる場合がある。このエラーが出る場合と出ない場合の条件の差異が不明(このあたりのメモリの使われ方は内部仕様の部分が多く詳細は公開されていない模様)。SQA/ESQAのエリアがギリギリの状態でメモリの使われ方か何かの要因でたまたまうまくいく場合とうまくいかない場合があると思われる。
</font>

### 調査/確認

過去事例調査。
この辺とか
https://groups.google.com/g/bit.listserv.ibm-main/c/NtK-dviVv-U

NIP処理中にSQA/ESQAのサイズ不足でエラーになっているらしい。
LOADxxにINITSQA の指定を追記してIPL, NIP処理用のSQA/ESQAサイズを増やすことで解決している例が多い。

### 参考

[z/OS - Statements and parameters for LOADxx](https://www.ibm.com/docs/en/zos/2.5.0?topic=sets-statements-parameters-loadxx)

>INITSQA
The INITSQA statement allows more SQA and ESQA storage to be reserved for IPL and NIP processing when the default amount is not sufficient. Increasing the minimum below the line SQA allocation must be done with caution to ensure that it does not affect the lower boundary of CSA storage. If a syntax error exists, the entire statement is ignored and message IEA368I is issued. 


### 対応措置

LOADxxのINITSQAを指定することで対応

```xxx:LOADxx設定例
...
INITSQA  0960K 0015M
...
```

<font color="red">
※INITSQAパラメーターを記述する場所が厳密に決まっているので注意！(詳細は上のマニュアル参照のこと)<br>
1番目のパラメーターは10桁目から始まる必要があります。<br>
各パラメーターは4桁の数字+(K or M)で指定する必要があります。<br>
二つのパラメーターの間の1バイトはブランクである必要があります。(ここに","を入れていたりするとエラーとなります。)<br>
</font>


設定変更後、WIBによるボリュームの抽出からやり直してリトライ。(何度か試してうまくIPLができてWazi aaSに接続できればそこで修正も可)


## Case6: TCP/IP Profile不備

### 事象
WIBを使用して作成したカスタム・イメージを元にWazi aaS仮想サーバーを作成し、起動しようとしたところ、IPLはできたが、TN3270やFTPなどサブシステムがポートをListenできない状況。

### 調査/確認

`D TCPIP,<TCPIPJobName>,NETSTAT,CONN`で確認すると、何もエントリーが表示されない状態。
マスターコンソールに以下のようなエラーが出力されていた。

```
EZZ6011I TN3270 BPX1SOC FAILED, RC = 00000070 RSN = 74580296
```

過去事例など確認すると、TLS設定を有効化したときに同様のエラーが生じているケースがあった。今回はTLSは使用しないはず。
TCP/IPのプロファイルを既存環境のものとWazi aaSインスタンスで使われているものを比較すると、TLS関連で不要と思われるパラメータの指定が追加されていた。

TCP/IPプロファイルは、WIB提供ユーティリティー zOS-cloud-prepare というツールで生成したものを使用していた。生成されたプロファイルをよく見てみると、`TCPCONFIG TTLS`が勝手に追加されていたり、自動起動のサーバーやポート番号などは既存の定義を全然引き継いでくれていなかったりした。
ATTLSの構成やPolicy Agentの起動などが勝手に含まれてしまっているために先のようなエラーが生じていたものと思われる。



### 参考

zOS-cloud-prepareというツールの位置づけが、マニュアル上かなり微妙な書き方になっている。

[Option 1: Using the zOS-cloud-prepare tool to automatically create z/OS configuration](https://www.ibm.com/docs/en/wazi-aas/1.0.0?topic=builder-setting-up-networking-premises-zos-environment#setting_up_networking__zos-cloud-prepare__title__1)
>You can automatically create a z/OS configuration (PARMLIB, PROCLIB, TCPPARMS, IPLPARM) by using the IBM-provided zOS-cloud-prepare tool. This tool runs in a running z/OS UNIX System Services environment and creates a new load suffix that contains a <font color="red">templates configuration</font> so that the z/OS can run in a Wazi as a Service environment successfully.

既存の構成をベースに作ってくれるのでそのまま使えそうにも見えるが、よーく見てみるとテンプレート構成を作ってくれる、という書き方になっている。
このツールを信頼しすぎてはいけない。


### 対応措置

zOS-cloud-prepare Toolで生成された各種プロファイルの見直しを実施。IPアドレスやデフォルト・ルートなどのシンボル化する部分についての情報以外は、きちんと現行構成に合わせて手動でカスタマイズする必要あり。

各種プロファイルを修正後、WIBによるボリュームの抽出からやり直し、リトライ。
結果意図したプロファイルでTCP/IPスタックが稼働し、各サブシステムのポートがListenされ、外部との通信も可能となった。PCOMからTN3270に接続しTSOに入れることも確認できた。
めでたしめでたし。

## Case7: z/OS側SSHユーザーの設定不備

### 事象
別のz/OS環境のVOLを抽出しようとしてWIB管理コンソールから「コンポーネントの作成」を実施しようとするとエラーが発生。

具体的には以下の画面でユーザーID、パスワードを入力してソース環境のz/OSにSSH接続すると...
![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3133323333312f63633234336534622d346261652d373538372d383038322d3432383532653462303066332e7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/ab1bd145-ab69-1b99-49af-4072963b82d5.png)

以下のようなエラーが表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/be765b4f-dfdb-a470-107e-a84909a98653.png)

>ZD&T 抽出ユーティリティー SFTP モジュールの zDTMainframeSFTP.jar の {0} バージョンの検査が、予期しない理由で失敗しました。
>**原因**: Checking {0} version for the ZD&T extraction utility SFTP module zDTMainframeSFTP.jar failed with unexpected reason.
>**コード**: 70991

このようなエラーが出るものの、OKをクリックすると次のボリューム選択の画面に進む。VOLUMEのリストも表示されるため、SSH接続、Extraction Utilityの実行が行えているように見える。
しかし、その後VOLUMEを選択してVOLUME抽出を行おうとすると失敗する。

### 調査

WIBサーバーのログを確認すると、以下のようなエラーが出ている。

<details><summary><font color="blue">WIBサーバーのログ</font></summary><div>

```log:/opt/ibm/wib/wib-server/logs/messages.log
[5/3/24, 7:24:44:027 JST] 00000034 com.ibm.zdt.utility.ZSystemRequestProcessesCommon            I [ZDT-LOG-INFO] zDTMainframeSFTP.jar version is expected to be: 14.1.0
[5/3/24, 7:24:45:486 JST] 00000034 com.ibm.zdt.utility.ZSystemRequestProcessesCommon            W [ZDT-LOG-WARN]  Running 'java -Xshareclasses:name=zdt-sftp -Xshareclasses:groupAccess -Xshareclasses:nonpersistent -Xshareclasses:nonfatal -jar /usr/lpp/IBM/zdt//zDTMainframeSFTP.jar --version' got error output: stdout: zd&t output starts here:
, stderr: <empty>
[5/3/24, 7:24:45:500 JST] 00000034 com.ibm.zdt.rest.v1.services.BaseJPAService                  E [ZDT-LOG-ERROR] checkZosUtilityVersion exception with no message.
com.ibm.zdt.utility.ZdtException,error code:70991
 ZdtResponse: Response code: 70991
com.ibm.zdt.utility.ZdtException,error code:70991
 ZdtResponse: Response code: 70991
        at com.ibm.zdt.utility.ZSystemRequestProcessesCommon.matchSFTPJarVersion(ZSystemRequestProcessesCommon.java:267)
        at com.ibm.zdt.rest.logic.ZSystemRequestProcesses.matchZosUtilityVersion_aroundBody8(ZSystemRequestProcesses.java:238)
        at com.ibm.zdt.rest.logic.ZSystemRequestProcesses$AjcClosure9.run(ZSystemRequestProcesses.java:1)
        at org.aspectj.runtime.reflect.JoinPointImpl.proceed(JoinPointImpl.java:179)
        at com.ibm.zdt.aspectj.AspectJHandler.logEntryExit(AspectJHandler.java:54)
        at com.ibm.zdt.aspectj.LoggingAspectRestAPI.around(LoggingAspectRestAPI.java:41)
        at com.ibm.zdt.rest.logic.ZSystemRequestProcesses.matchZosUtilityVersion(ZSystemRequestProcesses.java:232)
        at com.ibm.zdt.rest.v1.services.ZServices.checkZosUtilityVersion_aroundBody24(ZServices.java:1159)
        at com.ibm.zdt.rest.v1.services.ZServices$AjcClosure25.run(ZServices.java:1)
        at org.aspectj.runtime.reflect.JoinPointImpl.proceed(JoinPointImpl.java:179)
        at com.ibm.zdt.aspectj.AspectJHandler.logEntryExit(AspectJHandler.java:54)
        at com.ibm.zdt.aspectj.LoggingAspectRestAPI.around(LoggingAspectRestAPI.java:41)
        at com.ibm.zdt.rest.v1.services.ZServices.checkZosUtilityVersion(ZServices.java:1120)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at com.ibm.ws.jaxrs20.cdi.component.JaxRsFactoryImplicitBeanCDICustomizer.serviceInvoke(JaxRsFactoryImplicitBeanCDICustomizer.java:352)
        at com.ibm.ws.jaxrs20.server.LibertyJaxRsServerFactoryBean.performInvocation(LibertyJaxRsServerFactoryBean.java:642)
        at com.ibm.ws.jaxrs20.server.LibertyJaxRsInvoker.performInvocation(LibertyJaxRsInvoker.java:161)
        at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:101)
        at com.ibm.ws.jaxrs20.server.LibertyJaxRsInvoker.invoke(LibertyJaxRsInvoker.java:276)
        at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:213)
        at com.ibm.ws.jaxrs20.server.LibertyJaxRsInvoker.invoke(LibertyJaxRsInvoker.java:460)
        at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:112)
        at org.apache.cxf.interceptor.ServiceInvokerInterceptor$1.run(ServiceInvokerInterceptor.java:59)
        at org.apache.cxf.interceptor.ServiceInvokerInterceptor.handleMessage(ServiceInvokerInterceptor.java:96)
        at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:307)
        at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:127)
        at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:278)
        at com.ibm.ws.jaxrs20.endpoint.AbstractJaxRsWebEndpoint.invoke(AbstractJaxRsWebEndpoint.java:139)
        at com.ibm.websphere.jaxrs.server.IBMRestServlet.handleRequest(IBMRestServlet.java:148)
        at com.ibm.websphere.jaxrs.server.IBMRestServlet.doPost(IBMRestServlet.java:106)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:706)
        at com.ibm.websphere.jaxrs.server.IBMRestServlet.service(IBMRestServlet.java:98)
        at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1260)
        at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:748)
        at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:445)
        at com.ibm.ws.webcontainer.filter.WebAppFilterManager.invokeFilters(WebAppFilterManager.java:1361)
        at com.ibm.ws.webcontainer.filter.WebAppFilterManager.invokeFilters(WebAppFilterManager.java:1077)
        at com.ibm.ws.webcontainer.servlet.CacheServletWrapper.handleRequest(CacheServletWrapper.java:77)
        at com.ibm.ws.webcontainer40.servlet.CacheServletWrapper40.handleRequest(CacheServletWrapper40.java:87)
        at com.ibm.ws.webcontainer.WebContainer.handleRequest(WebContainer.java:969)
        at com.ibm.ws.webcontainer.osgi.DynamicVirtualHost$2.run(DynamicVirtualHost.java:283)
        at com.ibm.ws.http.dispatcher.internal.channel.HttpDispatcherLink$TaskWrapper.run(HttpDispatcherLink.java:1248)
        at com.ibm.ws.http.dispatcher.internal.channel.HttpDispatcherLink.wrapHandlerAndExecute(HttpDispatcherLink.java:470)
        at com.ibm.ws.http.dispatcher.internal.channel.HttpDispatcherLink.ready(HttpDispatcherLink.java:429)
        at com.ibm.ws.http.channel.internal.inbound.HttpInboundLink.handleDiscrimination(HttpInboundLink.java:569)
        at com.ibm.ws.http.channel.internal.inbound.HttpInboundLink.handleNewRequest(HttpInboundLink.java:503)
        at com.ibm.ws.http.channel.internal.inbound.HttpInboundLink.processRequest(HttpInboundLink.java:363)
        at com.ibm.ws.http.channel.internal.inbound.HttpInboundLink.ready(HttpInboundLink.java:330)
        at com.ibm.ws.http.channel.h2internal.H2StreamProcessor$Http2Ready.run(H2StreamProcessor.java:1811)
        at com.ibm.ws.threading.internal.ExecutorServiceImpl$RunnableWrapper.run(ExecutorServiceImpl.java:247)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
        at java.base/java.lang.Thread.run(Thread.java:839)
```

</div></details>

WIBサーバーで行われている操作をコマンド・レベルで疑似的に実行してみてうまくいくかどうかをチェックしていく。

1. WIBサーバー → z/OSへのSSH接続
    `ssh <zOS_SSHUser>@<zOS_HostName>` → OK
2. z/OSのUSSシェルで(1のSSH接続した状態で) javaコマンド実行
    `java -version` → <font color="red">NG (killされた)</font>
3. z/OSのUSSシェルで(1のSSH接続した状態で) zdtAuth環境変数確認
    `echo $zdtAuth` → OK (.profileに設定された値が表示される)
4. z/OSのUSSシェルで(1のSSH接続した状態で) sftpサーバーへの接続確認
    `sftp -P <portNumber> <sftpUser>@<sftpHostName>` → OK
    ※初めての接続時には`Are you sure you want to continue connection (yes/no)?`という確認が行われ、yesを返すとknown_hostsへの登録が行われ、2回目以降は聞かれなくなる。

z/OSのSSHユーザーでjavaが実行できない状態であったのが原因。

### 対応措置

対象のz/OSではJava17とJava8が導入されており、Java17の稼働に問題があったようなので、Java8を使用することで回避(SSHユーザーの.profileのPATH環境変数にてJava8のパスを指定)。

WIBサーバーからSSHでz/OSに接続し、以下のコマンドでバージョン情報が返されればOK
`java -jar /usr/lpp/IBM/zdt/zDTMainframeSFTP.jar --version`

※今回の対応としてはJava8を使用することで回避されましたが、これは恐らくJava8が32bit、Java17が64bitであることに起因した事象と思われます。64bitプロセスを実行しようとすると、その実行ユーザーのOMVSセグメントのMLIMITの値が0になっているとアドレス確保に失敗して類似の事象が発生します。対応としては、当該ユーザーのOMVSセグメントのMEMLIMITの値を適切に設定する必要があります。

## Case8: LOADxx不備

### 事象
WIB経由で作成したカスタム・イメージを元にWazi aaS仮想サーバーを作成し、サーバーを開始させると、IBM Cloudコンソール上は開始済みとなるが、z/OSは起動しない。
シリアル・コンソールも以下のエラーで接続できない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/735c7092-099a-62ef-b26c-140b57f373a6.png)

>コンソール接続エラー
コンソールが使用中か、まだユーザーがログイン可能になっていません。もう一度実行して、すべてのアクティブなコンソール・セッションを強制的に切断します。


※具体的なエラー情報が何も確認できない！

### 調査

何度か仮想サーバーの再起動や、ブラウザのキャッシュのクリアなども実施したが、状況は改善されない。
シリアル・コンソール(z/OSのマスター・コンソール)に接続できないので、IPLの初期段階、あるいはIBM Zエミュレーターの起動でエラーになっているものと推察される。
具体的な状況を確認するには、起動に失敗した仮想サーバーにアタッチされているデータ・ボリューム(ブロック・ストレージ)の中身を確認する必要がある。このボリュームをマウントして中身を確認するために別途仮想サーバーを立てる必要がある。
Wazi aaSは zLinuxがベースとなっているが、IA/Linuxからでもこのボリュームの参照は可能。今回WIBサーバーを稼働させるためのIA/Linux(RHEL)をIBM Cloud上の仮想サーバーとして用意していたので、このRHELを使用することにする。ログ取得の手順は以下の通り。

#### 1. Wazi aaS仮想サーバーからデータボリュームを切り離す

IBM CloudコンソールにてWazi aaS仮想サーバーを開き、データ・ボリュームを切り離す

<details><summary><font color="blue">画面イメージ</font></summary>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/81dbbd60-e6be-737a-082a-78f8f3762c01.png)

</details>


#### 2. 切り離したデータボリュームをRHELに接続

事前にRHELにsshで接続してブロック・デバイスの状況を確認しておく。

```xxx:lsblkコマンド例
[root@wibadm01 ~]# lsblk
NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
vda    252:0    0   250G  0 disk
tqvda1 252:1    0     1M  0 part
tqvda2 252:2    0   100M  0 part /boot/efi
mqvda3 252:3    0 249.9G  0 part /
vdb    252:16   0   390K  0 disk
vdc    252:32   0    44K  0 disk
vdd    252:48   0   500G  0 disk
mqvdd1 252:49   0   500G  0 part /data
```

IBM Cloudコンソールにて上で切り離したデータ・ボリュームをRHELサーバーにアタッチする。

<details><summary><font color="blue">画面イメージ</font></summary>

ストレージ・ボリュームの欄の接続をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/c88d5189-d614-c00c-c67e-c6f269eecec5.png)

上で切り離したデータ・ボリュームを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/9aa3b78b-c115-513a-f3d5-a75a1ee2d819.png)

データ・ボリュームが追加された状態
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/6ace40b8-1caf-d530-220c-a3da739e3102.png)

</details>


#### 3. RHEL上にデータ・ボリュームをマウントして起動失敗時のログを取得

RHELにsshで接続し、ブロック・デバイスの状況を再度確認。

```xxx:lsblkコマンド例
[root@wibadm01 ~]# lsblk
NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
vda    252:0    0   250G  0 disk
tqvda1 252:1    0     1M  0 part
tqvda2 252:2    0   100M  0 part /boot/efi
mqvda3 252:3    0 249.9G  0 part /
vdb    252:16   0   390K  0 disk
vdc    252:32   0    44K  0 disk
vdd    252:48   0   500G  0 disk
mqvdd1 252:49   0   500G  0 part /data
vde    252:64   0    66G  0 disk
```

最後の行(vde)が追加されたことが確認できる。このデバイスを適当なマウントポイントにマウントする。

```xxx:マウント例
[root@wibadm01 ~]# mount -t ext4 /dev/vde /mnt
```

マウントされたファイルシステムのツリー構造を確認

```
[root@wibadm01 ~]# cd /mnt
[root@wibadm01 /mnt]# tree --charset C .
.
|-- devmap
|-- env-overrides
|-- logs
|   |-- journal
|   |   `-- 7d9c2954605c44958ee376346accd6e1
|   |       `-- system.journal
|   `-- zpdt
|       |-- awsstart_p875_000001.log
|       |-- dv_0240_p907_000001.log
|       |-- dv_0241_p908_000001.log
|       |-- dv_0242_p909_000001.log
|       |-- dv_0700_p918_000001.log
|       |-- dv_5D20_p915_000001.log
|       |-- dv_5D21_p916_000001.log
|       |-- dv_5D22_p911_000001.log
|       |-- dv_5D23_p912_000001.log
|       |-- dv_5D24_p914_000001.log
|       |-- dv_5D2D_p917_000001.log
|       |-- dv_5D2E_p913_000001.log
|       |-- log_console_p875_t2024-05-09_23-20-25.txt
|       `-- tnportl2_p920.log
|-- lost+found
|-- MN25C1
|-- MN25T1
|-- MN25T2
|-- MN25U1
|-- MN25U2
|-- MN25W1
|-- properties.json
`-- ZM5D2E

5 directories, 25 files
```

これらのファイルのうち、以下のものを取得する。
- devmap
- env-overrides
- logs以下の全てのログファイル
- properties.json

logs/zpdt/log_console_xxx.txtファイルの中身を確認。

```log_console_xxx.txt
...
INFO: 050924 01:47:55: MIP: AWSMIP008I zArchitecture IPL mode (ZARCH ONLY=ON)
LOG : 050924 01:47:56: CPU: IPL started on CPU 0
LOG : 050924 01:47:56: CPU: IPR started for CPU 0
LOG : 050924 01:47:56: CPU: CPU reset
LOG : 050924 01:47:56: CPU: IPR complete for CPU 0
LOG : 050924 01:47:56: CPU: IPL bootstrap successful for device 9F50
LOG : 050924 01:47:57: CPU: Accepted code 0 cpu-0
LOG : 050924 01:47:57: CPU: Short PSW Change 00080008000668
LOG : 050924 01:47:57: KVM: KVM SET MP STATE: target CPU 0 state-7
INFO: 050924 01:47:57: STA: AWSSTA059I System Initialization complete
INFO: 050924 01:47:57: STA: AWSSTA012I All configured subsystem started
LOG : 050924 01:47:57: KVM: left cpu run loop rc=4
LOG : 050924 01:47:57: KVM: KVM SET MP STATE: target CPU 0 state-5
LOG : 050924 01:47:56: CPU: Disable Wait
INFO: 050924 01:47:57: EMI: AWSEMI307I Warning Disable Wait CPU 0 = 00020000 00000000 00000000 001000B1
...
```

上に抜粋したメッセージのうち、最後のメッセージからコード"0B1"でz/OSがDISABLE WAIT状態になっていることが確認できた。

`AWSEMI307I Warning Disable Wait CPU 0 = 00020000 00000000 00000000 001000B1`

末尾の `001000B1` に着目
参考: [Wait state codes in z/Architecture mode](https://www.ibm.com/docs/en/zos/2.5.0?topic=introduction-wait-state-codes-in-zarchitecture-mode)

この部分のさらに最後の3桁部分 `0B1` 部分がWait state codeを示す。

参考: [Wait state code: 0B1](https://www.ibm.com/docs/en/zos/2.5.0?topic=codes-0b1)
>System initialization cannot continue because of a problem in the LOADxx parmlib member, the I/O definition file (IODF) data set, or a device. 
The right-most four bytes of the program status word (PSW) have the following format: 
`xrr00www`
...
>The reason code (rr) is one of the following:
01
    <font color="red">The required data set was not found.</font>


抽出元のz/OS環境のLOADxxを確認すると、今回Wazi aaS用に用意したLOADxxのIODFの指定が間違っていたことにより、IODFのデータセットが見つからずにDISABLE WAIT状態になっていたことが分かった。


#### 4. RHELからデータボリュームをアンマウントし、切り離す

アンマウント

```
[root@wibadm01 ~]# umount /mnt
```

データ・ボリュームをRHELから切り離す。

<details><summary><font color="blue">画面イメージ</font></summary>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/7c10a069-ccee-045c-516c-0556328c62f2.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/f6d07232-9d1b-476f-0ee6-ebaae4d37855.png)

</details>


### 対応措置

抽出元z/OS環境にて、Wazo aaS用のLOADxxを正しく修正し、VOLUME抽出をやり直し(WIBコンソールにて、コンポーネントの編集画面から、変更されたVOLUMEのみ抽出)。
その後、イメージのデプロイ、仮想サーバーの作成をリトライ。

# 教訓
オンプレ環境のz/OSイメージをWIB経由でIBM Cloud上に複製する過程では、多種多様な要素技術が関与しています。各エリアの担当者も多数関わってくる話になります。
まだ成熟していない技術があったり、一方で頻繁にUpdateされていく部分もあったりします。そのような細かい部分の齟齬で一回でうまくいくことは少ないのではないかと思われます。すなわち、何度かTry&Errorを繰り返すことは必要だと考えておいた方がよさそうです。

既存z/OS環境を移植する際には、対象VOLを抽出してそれをWazi aaS用に加工する作業が入りますので、対象のVOLの数/サイズが、移植に要する時間に顕著に影響を及ぼします。
最初の1回目の移植手順を確立できるまでは、まずはできるだけ軽量な範囲に絞ってVOLを選定し、それで移植ができるか確認するのがよいでしょう。(例えば、最低限z/OSが起動して TSO, TCP/IP, TN3270あたりが稼働してPCOM接続確認ができるくらいのイメージ)

例えば、数時間かけて大量のVOLを吸い上げてようやくIBM Cloud上にWazi aaSインスタンスを作ったとして、TCP/IPプロファイルのどこか一か所不備があってネットワークがつながらないとなった場合、IBM Cloud側で修正する術が無いので、オンプレ側で修正して、VOLUMEの抽出、カスタムイメージの作成などをやり直さないといけません。(VOLUME抽出は変更が入ったものだけ抽出すればよいですが、カスタムイメージは1から作り直しです。)

一旦極小環境にて一気通貫でWazi aaSインスタンスが起動して接続できることが確認できてから、各種必要なVOLを含めた移植をする、というのがおすすめです。

# 参考: 処理時間

参考までに、どのくらいのVOL数/サイズの規模のものを移植した場合にどのくらいの時間がかかったか、だいたいの目安を記載しておきます。
ネットワーク通信速度やマシン性能にも依存しますのであくまで一つの目安程度に..

## コンポーネントの作成処理時間 (z/OS => SFTPサーバー)
z/OS上でExtraction UtilityによりVOLごとに圧縮ファイル(.gzファイル)を作成し、SFTPサーバーへ転送を行う処理

### SYSRES用ボリューム群
VOL数: 31本 
圧縮後サイズ: 113.65GB

**並行度=1**で抽出した場合の所要時間: <font color="red">約4時間18分</font>
速度: **約1.35GB/min**

※多くが3390-3型、一部9型が含まれる

### Data用ボリューム群
VOL数: 132本
圧縮後サイズ: 348.89GB

**並行度=4**で抽出した場合の所要時間: <font color="red">約26分</font>
速度: **約4.37GB/min**

※多くが3390-3型、一部9型が含まれる


### その他 (変更したVOLのみ抽出したケース)
VOL数: 2本(9型)
圧縮前サイズ: 17.8GB
圧縮後サイズ: ?? (8割くらいか？)

**並行度=2**で抽出した場合の所要時間: <font color="red">約20分</font>
速度: **約0.63GB/min**

## デプロイ処理(1) / SFTPサーバー => ICOS

SFTPサーバーからICOSにVOLファイルを転送し、IPLに必要なdevmapファイル等を生成する処理

VOL数: 163本 (SYSRES + Data)
圧縮後サイズ: 462.54

所要時間: <font color="red">約10分</font>
速度: **約46GB/min**

## デプロイ処理(2) / ICOS => カスタム・イメージ+スナップショット

吸い上げたVOL情報を、カスタム・イメージ+スナップショット(仮想サーバー作成の元になる形式)にフォーマット変換を行う処理

VOL数: 163本 (SYSRES + Data)
圧縮後サイズ: 462.54

Python処理所要時間: <font color="red">約30分</font>
速度: **約46GB/min**

カスタム・イメージ+スナップショット作成処理所要時間: <font color="red">約55分</font>
速度: **約8.4GB/min**