砂漠の旅人(たびと)

UNIX / MS-DOS 時代から電脳砂漠を旅しています

【RedfishViewer】Redfish のビューワを C# で作ってみる (WPF + Prism) - 後編

こんにちは、たびとです。

前回は RedfishViewer のインストールと最低限の操作について説明しましたが、 今回は開発の視点から、もう少し掘り下げて説明していきたいと思います。

ただし、ソースコードを中心とした技術解説ではなく、どこに苦労したのか、何を考えて機能を実装したのか、 といった設計よりの話題に重点をおいて説明していきたいと思います。

前回の記事はこちらです。

sabakunotabito.hatenablog.com

過去の記事を振り返ってみると、Redfish の情報を /redfish/v1 ルートから全て抽出したいとの思いから始まっていることが理解できます。 その流れで RedfishViewer の名前で C# 開発を開始しましたが、少しずつ機能を追加していって、ビューワ以上の機能を実装してしまいました。 しかしながら、実際に仕事でも使っていますが、9 割以上がビューワとして使うことが多いと思うので、そのままの名前でいいのかなと思います。その他の機能は、検証も不十分なところもあるので、オマケということにしときます。

この記事の対象者

  • Redfish ビューワに興味のある方、使ってみたい方
  • C# の MVVM を Prism フレームワークで作成したい方

RedfishViewer の動作

まずは App.xaml.cs ですが、メッセージ用ダイアログを RegisterDialog で定義していることと、 ViewModel 間で利用する Redfish やデータベースをアクセスするクラスを RegisterSingleton で定義してることを気にするぐらいです。 これらは、 interface 化する必要性はありませんが、RestSharp や EntityFramework とは異なるフレームワークを使いたいとき、 ここで差し替えるだけで変更することができるようになります。

実際、hal+json のとき、RestSharp と標準の HttpClinet の動作が異なったので、両者を差し替えて検証しました。 それで、HTTPリクエストヘッダ Accept を application/json から */* に変更した経緯があります。

RedfishViewer の画面構成

RedfishViewer は、起動時に MainWindow.xaml ウィンドウを呼び出し、 その中でUserControl である Reqests.xaml を表示します。 起動直後、Reqests.xaml は、メニューの最上位である Responses.xaml を呼び出し、メニューを切り替えると、 それに対応する UserContorol に表示を切り替えるようになっています。

これらは、以下のような 3 段階の階層構造になっています。 最下層の Responses.xaml, Responses.xaml, HttpErrors.xaml, Nodes.xaml, Configure.xaml, Tools.xaml はメニューと対応しています。

  • MainWindow.xaml : 唯一の Window で UserControl を表示するための枠
    • Reqests.xaml : メニュー表示とHTTPリクエストを入力する UserControl
      • Responses.xaml : HTTPリクエスト結果を表示する UserControl
      • HttpErrors.xaml : HTTPリクエストでエラーが発生した場合、エラー情報を表示する UserControl
      • Nodes.xaml : HTTPリクエストが成功した場合、ノード情報を表示する UserControl
      • Configure.xaml : カラーとネットワーク情報を設定する UserControl
      • Tools.xaml : URLデコードとエンコードを実行する UserControl

MainWindow

最初に MainWindow の View、ViewModel の順に呼ばれます。 ここでは、Reqests の UserControl を表示しています。

それと、Closing イベントを ViewModel で取得できるように定義していますが、 このに終了処理の取り消しを実装しようとしたのですが、CancelEventArgs の値を変更できなかったので、今回は諦めました。 ×ボタンを潰して、終了ボタンにすることも考えましたが、それも不格好なので却下しました。

Requests

HTTP リクエストを表示します。 最初は、GET メソッドと Basic 認証を実装していましたが、 POST メソッドで JSON ボディを送りたくなったので、ヘッダを追加しました。

当初は、HTTPリクエストとHTTP レスポンスは一つの UserControl で、 結果を表示するだけの簡単なアプリでしたが、メニュー化するときに分離しました。

分離したとき、最も悩んだのは、URI を入力し終わって、赤い魚アイコンまたは Enter キーを押したことを どうやって HTTP レスポンスの UserControl に通知するのか、IEventAggregator と理解できるまで悩みました。

以下、HTTP リクエストヘッダの全てを画面を切り取りながら合成しています。 起動時は HTTP リクエストは非表示で、画面右端のオレンジボタンがプラスのときに、 クリックすると、HTTP リクエストヘッダが表示されてマイナスに表示されます。

Requests 画面

Basic認証

ここで入力されたアカウント情報を RedfishRestSharp.csRequestAsync メソッドで利用します。 自己参照証明書でもエラーがでないように RemoteCertificateValidationCallback を定義しています。

HTTP Request ヘッダ

Prism による DataGrid の使い方を探せなかったので、試行錯誤して実装しました。 DataGrid のデータを ReactiveCollection<ParameterViewModel>ParameterViewModel で定義しているのが重要な箇所です。 これは、Model から ViewModel を作っています。 この ViewModel は、DataGrid の 1 行であり、セルの集合体でもあります。 ViewModel の中で、各セルに対して ReactiveProperty を設定するために、このような面倒な定義になっています。

ここでは、Redfish をリクエストするたびに、@odata.etag の内容を If-Match ヘッダに設定していることです。 ただし、自動検索したときは、意味をなさないため、If-Match ヘッダへの設定はスキップされます。

後、DataGrid 上で右クリックするとメニューが表示されて、行を追加することができます。 行削除は、チェックボックスをオフにすれば問題ないのと、利用頻度も低いと考えて未実装です。

HTTP Request パラメータ

仕事で使っている装置では使わないのですが、Redfish 以外でも使う可能性があったので、とりあえず追加しました。 基本的に、上述の HTTP Request ヘッダと同じ機能です。

HTTP Request Json ボディ

GET メソッド以外で、Redfish への入力パラメータとして最も利用頻度が高い機能です。 整形ボタンを押すと、JSON 文字列をインデントして、見易く表示します。

メニューボタン

メニューは、左上の三本線アイコンをクリックすると表示されます。 左矢印のアイコンを押すか、グレーアウトされた領域をクリックすると元に戻ります。

メニュー表示

Responses

メニューの RestAPI を選択すると、Responses の View を表示します。

もともと、Request と同じ UserControl だったので、入力と出力に分割したとき、 どうやって HTTP リクエスト情報を Request から Response へ送ることができるのか、 かなりの試行錯誤を経て IEventAggregator を使うことで実現できると理解できました。

Requests に定義された IEventAggregatorPublish メソッドを Response 側で定義した IEventAggregatorSubscribe メソッドで受け取ります。このとき、Publish の引数に HTTP リクエストの情報を詰め込んで渡しています。

HTTP レスポンス

リクエストの結果を右クリックするとメニューが表示され、「リクエスト反映」を選択すると、リクエスト時の全ての情報を上述の Request 画面に全て反映します。これも IEventAggregator の機能を使って渡しています。 また、「結果をクリア」を選択すると、DataGrid に表示しているデータをクリアします。

前回比較

この機能の有り無しで、かなりの効率化が実現できています。 RedfishViewer は、リクエスト結果をデータベースに保存するとき、現在分と過去分の 2 回分を保存できるようになっています。 ただし、過去分に保存するは、現在の値と異なる値があった場合のみです。 よって、リクエスト結果の DataGrid の「前回日時」セルに日付が入っていれば何らかの違いがあることになります。

この前回日時セルに違いがあるとき、前回比較タブを選択すると、以下のように異なる箇所がハイライト表示されます。

ここで、最後まで悩んだのは、DataGrid は「現在から過去へ」表示しているのに、比較画面は「過去から現在へ」へと表示していることです。本来は「過去から現在へ」と統一したかったのですが、DataGrid のセルが長くなった場合に、前回比較に値があることが少ないため、見辛いことが多くなることです。 このため、実用性を重視して、左右で異なる順序で表示しています。

現在レスポンスと過去レスポンスの比較

前回比較は、以下のサイトを参考にしてください。

github.com

入出力ヘッダ

ここは、入出力のヘッダ情報を表示しているだけです。この情報はデータベースにも保存されています。 実装的には、HTTP リクエストの使いまわしなので、特にコメントする必要はありません。

HTTP リクエスト と HTTP レスポンスのヘッダ情報

キーワード検索

ブラウザと同じように URI 文字列ではないときに、キーワード検索として利用することができます。 これは、DataGrid に表示されたレスポンスが対象となります。

キーワードおよび URI は 100 件分を保存しており、100 件を超えると古い情報から削除されていきます。 また、キーワードはデータベースにも保存されるため、RedfishViewer を終了しても消えることはありません。

キーワード検索

自動検索モード

前回の説明通り、左上部の GET メソッドの横のトグルボタンをオンにすると自動検索モードになります。 これは、GET メソッドのとき有効で、リクエストした結果の JSON 内の @odata.id から URI を生成し、 再帰的に情報を取得していきます。

この処理は何度も作り変えました。当初 DispatcherTimer を使って実現していまいたが、 IEventAggregator の変更に合わせて、素直な方式に修正したので簡易になっています。

自動検索すると、画面右下にトースト形式のメッセージが通知されます。 これも当初はマテリアルデザインに付属の Snackbar を使っていましたが、 自動検索後もメッセージが延々と表示されるため、Notification に変更しました。 詳細は以下を参照してください。

github.com

また、自動検索を実行中に、トグルボタンをオフにすると、以下のように 自動検索モード を中止することができます。 ただし、ダイアログが表示されている間もリクエストは実行され続けるので、ここは改善するかを検討する必要がありそうです。

自動検索モードの中止

HttpErrors

ここでは、400番以上をエラーにするとか自前でエラーを制御している訳ではなく、 RestSharp のレスポンスに例外が発生していた場合、エラーとしての処理を実施しています。

リクエストが失敗すると、以下のようにエラー画面に遷移します。エラーとなる条件は、 このとき、DataGrid を右クリックすると、レスポンスの画面と同じように リクエスト反映 メニューを選択すると、 再度同じリクエストを実行することができます。

HTTP リクエストエラー

Nodes

リクエストが成功した場合、ルート URI をキーとしてノード情報が保存されます。 ノード情報は DataGrid を利用していて、タイトル、概要、備考の 3 項目については編集することができます。

自動的にデータを保存するようにしたかったのですが、DataGrid の編集を確定させる方法が探せなくて、 やむを得ず 保存 ボタンを採用しています。

ノード情報

プラグイン

当初、ここに表示されたノード情報はブラウザからもアクセスすることがあるため、 ブラウザから開くことができると便利だということから実装したのが始まりです。

プラグインにした理由は単純で、仕事で使っているサーバをシングルサインオン (SSO) でアカウントの入力なしにブラウザへ連携したかったからですが、この SSO の接続方式は公開情報ではないため、今回の GitHub からは除外されています。 プラグインのボタンが 2 つあるのも、BMC の WebUI 接続用と OS 操作画面への接続用と、それぞれ用途が異なるためです。

プラグインの作り方は、Plugins フォルダを参照し、起動時のプラグインの読み込みは、RedfishRestSharp.csLoadPlugin メソッドを参照してください。 公開できなくて残念ですが、先程の仕事で使う SSO 用のプラグインは、プラグイン用プロジェクトとして作成し、LoadPlugin メソッドで取り込んで利用できるようになっています。

アカウント反映

ここに表示されたアカウント情報を HTTP リクエストの Basic認証 のアカウントに設定します。 パスワードを複雑にしている場合は、地味に便利な機能です。 この処理も IEventAggregator を使って、NodesViewModel から ReqestsViewModel へ通知します。

DB から読み込む

データベースに登録済みのデータから、この URI を含むデータを全て取り込んで、リクエスト結果に表示します。 検索を合わせて使うことにより、かなりの時間短縮に貢献してくれている必須の機能です。 この処理も IEventAggregator を使って、NodesViewModel から ResponsesViewModel へ通知します。

DB から削除する

滅多に使わない機能ですが、データベースに登録された URI 情報を含むデータを削除します。 元に戻すことはできないので、削除前にメッセージを表示するようになっています。

データ削除の確認

Configure

ここでは、配色とネットワークに関して変更することができます。

テーマとネットワーク

テーマ

ここは説明不要だと思います。 色の調整も MaterialDesignThemes にある機能で、オマケ程度と思ってください。

  • ダークモード : ライトとダークを切り替えます。
  • 色の調整 : 色のコントラストによっては、目に優しい配色に変更するようです。
  • プライマリ色 : MaterialDesignThemes で提供されている色の中から好きな色を選択します。
  • セカンダリ色 : MaterialDesignThemes で提供されている色の中から好きな色を選択します。

ネットワーク

Redfish のリクエストによっては、長時間待つ必要があるので、タイムアウトの変更は必須でした。 初期値が 3600 秒 (1 時間) なのも、リクエスト発行後に気付いて変更するのが、面倒だからです。

当時、3 ~ 5 時間待つリクエストを検証するとき、Postman のタイムアウトを無制限に設定し、 リクエストを発行したのに、タイムアウトしたときには驚きました。 curl で実行するもの面倒だったので、開発中の RedfishViewer を使ったことは良い思い出です。 Postman のタイムアウト無制限問題は、利用環境が悪かったのか、版数によるものか、正しく検証できていません。 滅多に使わない機能なので優先度は低いですが、気が向いたら、再度検証してみたいと思います。

Tools

ここは Redfish とは何も関係なく、いま欲しい機能を実装しました。 会社は Microsoft 製品で統一されていて、SharePoint Online にドキュメントが格納されていますが、 それをメールに貼り付けられると、以下のような URL エンコードされた状態になります。

このとき、同じ場所に別ファイルを参照したり、格納したりする頻度はかなり高く、 インタネット上によく見かける URL デコードサービスを使うことを躊躇します。

そこで、常時起動している RedfishViewer に実装してしまおうと思って実装しました。 デコードしか使いませんが、エンコードとセットにしています。 ただし、漢字コードは気にしていないため、UTF-8 以外だと文字化けするかもしれません。

URL デコードとエンコード

ここは勢いでサッと作った機能ですが、 これから Prism を始めようとしている方は、このソースコードから調べた方が良いと思います。

Log2Console による動作確認

Prism は、Log2Console を使うと動作を解析するのに役に立ってくれると思います。 オリジナルは詳細画面で日本語が文字化けするので、日本語対応版を使った方が思います。

github.com

インストール後、Log2Console を起動し、ツールバーから Receives... をクリックし、 'TCP (IP v4 and v6)' を選択します。 ここでは何も変更せずに IPv4 でデータを受信できるようにします。

Log2Console のネットワーク設定

次に、再度 'TCP (IP v4 and v6)' を選択し、'Use IPv6 Address' を True に設定します。 これにより、IPv6 でデータが受信できるようになります。

IPv6 の設定

この状態で、RedfishViewer を実行すると、以下のようにトレースレベル以上のログが記録されていきます。 右側にはクラスがツリー構造で表示されるため、RedfishViewer の構成を把握するのに便利だと思います。 また、GitHubソースコードをダウンロードし、Visual Studio からデバッグモードで実行しても同じように利用することができます。 ブレークポイントと組み合わせると、より便利に使うことができると思います。

RedfishViewer のトレース

まとめ

当初の予想に反して、かなりの長文になってしまいました。 RedfishViewer も当初は /redfish/v1 から再帰的にデータを取得したい、との思いから始まり、 気付いてみると、仕事でも実用耐えるレベルのアプリになってしまいました。

今回が初の Prism アプリなので、まだまだ改善の余地があると思いますが、 いくつか作りたいアプリがあるので、しばらくは保留にすることにします。 Prism のソースコードの解析については、気が向いたら説明の記事を書くかもしれません。

それと、ソースコードを解析しようと思っている方は、Log2Console を使うことをお勧めします。 本当に便利なので、仕事にも積極的に採用して欲しいものです。 このブログでも Log2Console で検索すると、いくつか記事がヒットするので、興味があれば参考にしてみてください。

では、皆さん、よい旅を。