Zoweと戯れる - (2) Zoweデスクトップ


# はじめに
Zoweの手始めとして、すぐに使えるZoweデスクトップというのがあるので、まずはそれを軽くさわってみます。

## 関連記事
[Zoweと戯れる - (1) インストール](https://qiita.com/tomotagwork/items/e0fdb744f9c30fc224ed)
[Zoweと戯れる - (2) Zoweデスクトップ](https://qiita.com/tomotagwork/private/cc06368abf3a8caaaac5)
[Zoweと戯れる - (3) Zowe CLI](https://qiita.com/tomotagwork/private/78cd7c63dd00a29564a4)
[Zoweと戯れる - (4) VSCode Extension for Zowe](https://qiita.com/tomotagwork/items/8cdc99e669f8ae82eddc)

# PC側環境

OS: Windows10
ブラウザ: Chrome or Firefox

# カスタマイズ
ちょろっと使ってみようとしたら動かない所があり、そのためにいくつか対応した部分があるのでそのメモです。

## ホスト名の名前解決
どうやらこのブラウザのインターフェースは、JavaScriptベースで、この中のサブウィンドウから直接HTTPリクエストが飛んでいるっぽい。その時Zoweのサーバーに設定されているホスト名で内部的にRESTリクエスト(HTTPリクエスト)が発行されているようなので、クライアントPC側でそのホスト名の名前解決ができないといけらしい。
DNSは使っていない環境なので、ローカルPCのhostsに、ZOWEのホスト名(今回の場合zdtpe01.mk.ise.com)とIPアドレスのマッピングを作成してあげないとうまくサービスが起動しませんでした。
Windows環境で実施してるので、以下の感じにホスト名を指定。(IPアドレス部分はマスキングしてます。)

```xxx:\Windows\System32\drivers\etc\hosts
xxx.xxx.xxx.xxx zdtpe01 zdtpe01.mk.ise.com 
```




## 証明書関連

USS Explorerを利用しようとしたらうまく動きませんでした。内部的にこんな感じのエラーが出てました。

```
Request URL: https://zdtpe01.mk.ise.com:7554/api/v1/zosmf/restfiles/fs?path=/u
Request Method: GET
Status Code: 502 
Remote Address: xxx.xxx.xxx.xxx:7554
Referrer Policy: no-referrer-when-downgrade
```

```
{"messages":[{"messageType":"ERROR","messageNumber":"AML0105","messageContent":"The certificate of the service accessed by HTTPS using URI '/api/v1/zosmf/restfiles/fs' is not trusted by the API Gateway: Certificate for <ZDTPE01.MK.ISE.COM> doesn't match any of the subject alternative names: []","messageKey":"apiml.common.tlsError"}]}
```

ググるとこの辺が引っかかってきました。
https://github.com/zowe/zowe-install-packaging/issues/279

とりあえずテスト環境なので証明書のチェックオフってしまえということで、以下を参考に。
[Zowe API Mediation Layer Security - Certificate management in Zowe API Mediation Layer - Zowe runtime on z/OS - Disable certificate validation](https://zowe.github.io/docs-site/latest/extend/extend-apiml/api-mediation-security.html#zowe-runtime-on-z-os)

以下のスクリプトそれぞれについて、

$ZOWE_RUNTIME/api-mediation/scripts/api-mediation-start-catalog.sh
$ZOWE_RUNTIME/api-mediation/scripts/api-mediation-start-discovery.sh
$ZOWE_RUNTIME/api-mediation/scripts/api-mediation-start-gateway.sh

以下のパラメータをfalseに設定し直し、ZOWE再起動します。
```-Dapiml.security.verifySslCertificatesOfServices=false```


# 使用イメージ

ブラウザから以下にアクセスします。
https://zdtpe01.mk.ise.com:8544

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/064e472f-2941-6638-2dd1-1fa2aa61e42f.png)

ログインすると以下のようにブラウザ上に専用のデスクトップのような画面が表示されます。このブラウザの中のデスクトップで各種機能のサブ画面が開いて、様々な操作ができるようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/fd3139a4-8445-3d23-6ec1-d5b290d85e68.png)

それぞれの機能の画面イメージを見ていきます。

## MVS Explorer

MVS上のデータセットの参照、編集ができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/4273b95d-6d2d-a104-c1b4-4c5d3eb251e0.png)

JCL, REXXについては構文チェック、アシスト機能などがあるようです。(エディターで文書開いた後、右上のリストからタイプを選択。選択できるのは、REXX, JCL, TEXTの3つ)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/2e5588c8-8059-b4f7-8aba-a0ed91f13cd5.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/49ca7010-bbfe-c991-8b8e-9882bbb0a8df.png)


## JES Explorer

JESのSpoolの内容が確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/c53d1cb6-7943-6691-25ba-8ec6cf8fbe18.png)

検索条件として指定できるのは、Owner, Prefix(Job名), JobID, Status(ACTIVE/INPUT/OUTPUTから選択)の 4つ。
PURGEはできるが、ここからJOBのサブミット(JS)はできない模様。

## USS Explorer

これもRexx, JCLについては構文解析してくれます。(Rexx, JCL, Textから選択)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/272538d0-fc20-647d-e2da-e90138292dba.png)


## Editor

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/9c29e16d-a658-d023-eea5-626300f4d9d7.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/ec3377b6-71f1-665e-e778-054c79996374.png)

Datasetsの方は(Beta)と書いてあるせいか、データセット名を全部入れないとメンバーの候補が出てこなかった。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/04422c5a-00b9-e156-8be5-0f4fead40207.png)

こちらは、VSCodeっぽい雰囲気の、リッチなテキストエディターという感じ。構文解析できる言語の種類も豊富。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/561a3450-16c2-9c5d-f355-87cf5bd045ef.png)
これ以降も、3倍くらい言語のリストが続いています。
Swift, Typescript, Visual Basicとかあるのに、なぜかCOBOL, PL/Iは無い^^。オープン系のエディタをそのままポーティングしてる感じ(それこそVSCode流用してるんじゃなかろうか)。
※別の記事にしますが、VSCodeのZowe Extensionというのもあって、個人的にはそっちが本命だと思ってます。

## VT Terminal

SSH/telnetで接続できる端末機能。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/7115f207-186f-36e8-eeb4-5e43d10f7a5f.png)

デフォルトだとHost名のところが"localhost"になっていてつながらない。
IPアドレス指定するとつながった。(ホスト名指定だとNG)
このあたりのネットワークの要件がイマイチ不明。
SSHクライアントはホスト側で動いていて、結果を画面に表示しているだけっぽい。
デフォルトのキーマッピングが謎で、バックスペースは効かなかったし、vi起動しようとしたらCEEのエラー出てこけました。
キーマッピングどうやって変更するのか(そもそもできるのか)、メニューらしきものが一切無いのでわかりませんでした(マニュアル読んでないが、この手のものはマニュアル探さないといけない時点でアウトだと思います)。
普通にteratermでSSH接続した方が100倍使い勝手が良さそう。

## TN3270

最初、TN3270端末はどうもうまくつながらなかった。
色々試して、z/OSと同じネットワーク上(つまり余計なFirewallとか無い環境)のLinuxマシンを使って、そのLinux上のFirefoxから試したらうまくいった。こっちの環境だとVT端末もホスト名で接続できた(Linux側に要hosts指定)。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/cb0d254b-6bf8-3134-32e1-c089414b840e.png)

※うーん、ブラウザとz/OS間にFirewallが絡んでいるのだが、そこが怪しい。ZoweデスクトップはWebSocketでのデータ送受信が行われているようだが、この通信がうまくいっていないかもしれない(昔、WebSocket絡みで似たような事象があった...)。あとはzD&Tなのでそこのネットワークの構成も特殊といえば特殊。このあたりは、今回使っている環境依存の部分がありそうです。



## API Catalog
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/7e73dd6b-a329-c4b9-8d8f-62706adda81e.png)

API Mediation Layer API

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/ea302c31-dc2b-afa4-6db0-a390d5f920d9.png)

https://zdtpe01.mk.ise.com:7554/api/v1/apicatalog/apidoc/apicatalog/v1

```json:swagger文書
{

    swagger:2.0
    info:{
        description:REST API for the API Catalog service which is a component of the API Mediation Layer. Use this API to retrieve information regarding catalog dashboard tiles, tile contents and its status, API documentation and status for the registered services. [Swagger/OpenAPI JSON Document](https://localhost:7554/api/v1/apicatalog/apidoc/apicatalog/v1)
        version:1.0.0
        title:API Catalog
    }
    host:localhost:7554
    basePath:/api/v1/apicatalog
    tags:[
        0:{
            name:API Catalog
            description:Current state information
        }
        1:{
            name:API Documentation
            description:Service documentation
        }
    ]
    schemes:[
        0:https
    ]
    paths:{
        /apidoc/{service-id}/{api-version}:{
            get:{
                tags:[
                    0:API Documentation
                ]
                summary:Retrieves the API documentation for a specific service version
                description:Returns the API documentation for a specific service {service-id} and version {api-version}. When the API documentation for the specified version is not found, the first discovered version will be used.
                operationId:getApiDocInfoUsingGET
                produces:[
                    0:application/json;charset=UTF-8
                ]
                parameters:[
                    0:{
                        name:service-id
                        in:path
                        description:The unique identifier of the registered service
                        required:true
                        type:string
                    }
                    1:{
                        name:api-version
                        in:path
                        description:The major version of the API documentation (v1, v2, etc.)
                        required:true
                        type:string
                    }
                ]
                responses:{
                    200:{
                        description:OK
                        schema:{
                            type:string
                        }
                    }
                    401:{
                        description:Unauthorized
                    }
                    403:{
                        description:Forbidden
                    }
                    404:{
                        description:URI not found
                    }
                    500:{
                        description:An unexpected condition occurred
                    }
                }
                deprecated:false
            }
        }
        /containers:{
            get:{
                tags:[
                    0:API Catalog
                ]
                summary:Lists catalog dashboard tiles
                description:Returns a list of tiles including status and tile description
                operationId:getAllAPIContainersUsingGET
                produces:[
                    0:application/json;charset=UTF-8
                ]
                parameters:[]
                responses:{
                    200:{
                        description:OK
                        schema:{
                            type:array
                            items:{
                                $ref:#/definitions/APIContainer
                            }
                        }
                    }
                    401:{
                        description:Unauthorized
                    }
                    403:{
                        description:Forbidden
                    }
                    404:{
                        description:Not Found
                    }
                }
                deprecated:false
            }
        }
        /containers/{id}:{
            get:{
                tags:[
                    0:API Catalog
                ]
                summary:Retrieves a specific dashboard tile information
                description:Returns information for a specific tile {id} including status and tile description
                operationId:getAPIContainerByIdUsingGET
                produces:[
                    0:application/json;charset=UTF-8
                ]
                parameters:[
                    0:{
                        name:id
                        in:path
                        description:id
                        required:true
                        type:string
                    }
                ]
                responses:{
                    200:{
                        description:OK
                        schema:{
                            type:array
                            items:{
                                $ref:#/definitions/APIContainer
                            }
                        }
                    }
                    401:{
                        description:Unauthorized
                    }
                    403:{
                        description:Forbidden
                    }
                    404:{
                        description:Not Found
                    }
                }
                deprecated:false
            }
        }
    }
    definitions:{
        APIContainer:{
            type:object
            properties:{
                activeServices:{
                    type:integer
                    format:int32
                }
                createdTimestamp:{
                    $ref:#/definitions/Calendar
                }
                description:{
                    type:string
                    description:The description of the API
                }
                id:{
                    type:string
                    description:The API Container Id
                }
                lastUpdatedTimestamp:{
                    $ref:#/definitions/Calendar
                }
                services:{
                    type:array
                    description:A collection of services which are registered with this API
                    items:{
                        $ref:#/definitions/APIService
                    }
                }
                status:{
                    type:string
                    description:The Status of the container
                }
                title:{
                    type:string
                    description:The API Container title
                }
                totalServices:{
                    type:integer
                    format:int32
                }
                version:{
                    type:string
                    description:The version of the API container
                }
            }
            title:APIContainer
        }
        APIService:{
            type:object
            properties:{
                apiDoc:{
                    type:string
                    description:The API documentation for this service
                }
                description:{
                    type:string
                    description:The description of the API service
                }
                homePageUrl:{
                    type:string
                    description:The service home page of the API service
                }
                secured:{
                    type:boolean
                    description:The security status of the API service
                }
                serviceId:{
                    type:string
                    description:The service id
                }
                status:{
                    type:string
                    description:The status of the API service
                }
                title:{
                    type:string
                    description:The API service name
                }
            }
            title:APIService
        }
        Calendar:{
            type:object
            properties:{
                calendarType:{
                    type:string
                }
                firstDayOfWeek:{
                    type:integer
                    format:int32
                }
                lenient:{
                    type:boolean
                }
                minimalDaysInFirstWeek:{
                    type:integer
                    format:int32
                }
                time:{
                    type:string
                    format:date-time
                }
                timeInMillis:{
                    type:integer
                    format:int64
                }
                timeZone:{
                    $ref:#/definitions/TimeZone
                }
                weekDateSupported:{
                    type:boolean
                }
                weekYear:{
                    type:integer
                    format:int32
                }
                weeksInWeekYear:{
                    type:integer
                    format:int32
                }
            }
            title:Calendar
        }
        Mono«ResponseEntity«string»»:{
            type:object
            title:Mono«ResponseEntity«string»»
        }
        TimeZone:{
            type:object
            properties:{
                displayName:{
                    type:string
                }
                dstsavings:{
                    type:integer
                    format:int32
                }
                id:{
                    type:string
                }
                rawOffset:{
                    type:integer
                    format:int32
                }
            }
            title:TimeZone
        }
    }

}
```

ふーむ。
https://zdtpe01.mk.ise.com:7554/api/v1/apicatalog/containers/
これで、登録されているAPIの一覧とかステータスとか確認して、

https://zdtpe01.mk.ise.com:7554/api/v1/apicatalog/apidoc/<service-id>/<api-version>
これで、swagger文書取得できますってことなのかな？

```https://zdtpe01.mk.ise.com:7554/api/v1/apicatalog/apidoc/datasets/v1 ```のレスポンス

```json:
{

    swagger:2.0
    info:{
        description:IBM z/OS Datasets REST API service
        version:1.0.0
        title:IBM z/OS Datasets
    }
    host:localhost:7554
    basePath:/api/v1/datasets
    tags:[
        0:{
            name:apimlHidden
        }
    ]
    schemes:[
        0:https
    ]
    paths:{
        /apimlHidden:{
            get:{
                tags:[
                    0:apimlHidden
                ]
                parameters:[]
                responses:{
                    200:{
                        description:OK
                    }
                }
            }
        }
    }
    externalDocs:{
        description:External documentation
        url:https://ZDTPE01.MK.ISE.COM:8547/swagger-ui.html
    }

}
```
ま、この辺はデスクトップで使うというより、APIでクライアントアプリ作ったりするお話なので、おいおい勉強していきます。


# おわりに
Zoweデスクトップとしての売りは、ブラウザ"だけ"で色んな操作ができるというところなんでしょう。
PCOM、z/OS Explorer(eclipse), teraterm　あたりを個別に導入しなくても、ブラウザだけで一通りの操作ができます。
イメージとしては、VMWare上の仮想マシンに対してブラウザコンソールからアクセスしてGUIの画面を操作する、あるいは、Windowsのリモートデスクトップで操作する感覚に近いでしょうか。

【プラス面】
PC側にブラウザだけあればよい。PCOMのライセンスいらないのは大きいかも。
独自のツールを作りやすい。(各種操作がAPIとして提供されていたりする)

【マイナス面】
どこまでこいつをゴリゴリ使うかによると思いますが、ブラウザでの操作性はかなり厳しい気がする。
例えば、Alt+TabキーでWindow切り替えたりしたいのですが、いかんせん全てブラウザ内のサブウィンドウなので、そういうショートカット制御ができません。それから、TN3270に関しては、ファンクションキーを押したときにブラウザ自体も反応してしまったりして(F3でブラウザの検索窓が出てきちゃったり...)、ウザいです。まぁ制御する術はありそうですが。
やはり専用ツール(PCOM, eclipse, teraterm, VSCode)にはかなわないでしょう。

【その他】
ローカルのPC上とのやり取りはできなさそう(ファイルアップロードとかダウンロードとか)。
たいてい、Ctrl+c, Ctrl+vでコピペはできる。ローカルPCとの間でのコピペも可。

まぁ、このZoweデスクトップ機能がZoweとしての本命ではないと思っているので、補足的に使うのであれば充分というか、結構リッチな機能提供してくれているんじゃないでしょうか。