Postmanを使ってREST APIで物理サーバーを操作する(入門編)
はじめに
最近(といってももはやここ10年弱くらい)の物理サーバーがREST APIで操作できるようなっているのご存知でしたか?メーカー側も世の中のトレンドにあわせて便利に使ってもらえるように製品開発を進めているわけです。なのでサーバーの電源オン・オフなどの操作も昔からあるIPMIやSSH経由のSM-CLP(SMASH)だけでなく、CurlコマンドやPostmanで実行したりPythonなどで通常のWeb APIを扱うのと同じ感覚でスクリプトを書いたりもできたりします。
今後数回の投稿でサーバーのリモート管理の話を情報共有していこうかなと思います。
投稿一覧
第1回: BMC(Basebord Management Controller)全般について
第2回: サーバー管理用REST API (DMTF Redfish)の基本的な使い方と、Redfishを使うときに便利なPostmanの変数・スクリプトの使い方(本投稿です)
第3回: PostmanとREST API(Redfish)を使ったサーバーの具体的な操作やスキーマの見方など
第4回(予定): ファームウェア更新やBIOS設定変更、イベントやテレメトリーなど
Redfish APIについて
サーバー管理用のREST APIですが、メーカー各社や製品ごとにAPIがバラバラだとユーザーの皆様にとって不便なので業界の標準団体DMTF (Distributed Management Task Force)がRedfish APIとして標準化しており、各社ともそれに準拠した実装をしています。
IPMIが標準でできることが限られていて、結果的に各社独自のOEM拡張がかなり多くなってしまったことや、OEM拡張は16進数のオプションと戻り値を使うrawコマンド形式で非常に使いづらかったことを反省し、Redfish標準にも各社が自由にできるOEM拡張の仕組みはあるものの、標準部分を充実させて各社にOEM拡張を使う動機を与えないようにすることがデザイン目標の一つとなっています。
Redfishの主な特徴は
- RESTfulなインターフェースとデータモデル表現を用いた管理標準
- モデル指向なのでコンポーネント間の関係やセマンティクスを表現可能
- データモデルは相互運用可能なスキーマで定義。OpenAPI YAML、OData CSDL、JSONスキーマの各フォーマットで公開中
- JSONを使うのでプログラミング環境との統合が簡単、かつ人間にも読みやすい
Redfishは/redfish/v1
がサービスルートで、ざっくり以下の図のようなリソースマップで表されます。普通に使う場合に重要なのは以下の6つくらいですね。
-
/redfish/v1/Chassis
: サーバーの物理構成 -
/redfish/v1/Systems
: サーバーの論理構成 -
/redfish/v1/Managers
: Redfishサービスを提供するBMC設定(BMCのIPアドレスなど) -
/redfish/v1/SessionService
: Redfish含めたセッション管理 -
/redfish/v1/Task
: すぐに終わらない処理を管理(ファームウェア更新など) -
/redfish/v1/Event
: イベントのサブスクリプション
実際にRedfishを使ってみる
サービスルート
まずPostmanでサービスルート(/redfish/v1)にアクセスしてみます。サービスルートはRedfishサービスで唯一認証が必要なく、サービスルート直下にどんなリソースが実装されているか確認できます。基本的には先ほどの図のリソースが返ってくるはずです。また、各リソースはリンクで辿れるようになっているので便利です。
ちなみにURIの{{baseUrl}}
というのはPostmanの変数で管理対象のサーバーIPアドレスを設定しています。サーバーが複数台あるときに管理対象を切り替えるのに便利です。
ユーザー認証
サービスルート(/redfish/v1)は認証が必要ありませんが、それ以外のURIにアクセスするにはユーザー認証が必要です。認証に失敗すると401 Unauthorized
のHTTPステータスコードが返ってきます。
ユーザー認証にはベーシック認証(Basic Auth)またはセッションを確立する方法があります。
単発の操作はベーシック認証でも構いませんが、続けて色々と操作する場合にはセッションを確立します。ベーシック認証で連続操作を行うと、ログが認証で埋め尽くされ、他の重要なイベント(たとえばハードウェアの故障など)が埋もれて見落とすことになってしまったりします🤭
ベーシック認証
ベーシック認証は単純明快ですね。Authorizationタブで認証タイプにBasic Auth
を選び、ユーザー名とパスワードを設定すればOKです。
セッション作成
セッションを作成するには/redfish/v1/Sessions
にユーザーとパスワードをJSONのペイロードとしてPOSTします。成功すると201 Created
のステータスコードが返ってきます。なお、以下のスクリーンショットは緑の点線より上がリクエストで、点線より下がレスポンスです。
セッションを作成するとレスポンスヘッダーで認証トークンとロケーションURIが返ってくるので確認しましょう。
-
X-Auth-Token
: これ以降のAPIリクエストのヘッダーで指定します -
Location
: このURIにDELETE
をリクエストすることでRedfishのセッションを終了します。
なお、ここではX-Auth-Tokenは隠していませんが、本番環境では不正なユーザーに知られないようにご注意ください。
セッション終了
作成したばかりですが、練習ということでセッションを終了してみます。リクエストヘッダーにX-Auth-Token
を指定して、Location
に対してDELETE
をリクエストします。セッション終了が成功すると200 OK
のステータスコードが返ってきます。
Postmanの変数とスクリプト
以上、サービスルートを眺めてセッションを作成・終了してみましたが、X-Auth-Token
やLocation
を毎度コピペするのは面倒ですね。
Postmanでは変数が使えるので、レスポンスで返され値を自動で変数に設定し、リクエストのヘッダーやURIにあらかじめ変数を設定しておけばコピペなしで使えて便利ですね。
Postmanの変数には何種類かのスコープがありますが、Redfishのレスポンスボディーで渡されたURIをクリックして新しいリクエストを投げるような使い方をする場合はグローバル変数を使うのが楽です。(環境変数やコレクション変数だと、いちいちコレクションに保存しないといけないので面倒です。)
ただし、リクエストを保存していって他の人とコレクションをシェアするような場合はコレクション変数を使った方が変数がなくて動かないというエラーがなくて親切ですね。
Postmanにはリクエストの前処理として実行できるPre-request Script
タブと後処理として実行できるTests
タブがあります。
今やりたいことは、セッションを作成したときのレスポンスヘッダーで渡されるX-Auth-Token
とLocation
の値を変数に設定したいので、セッションを作成するリクエストのTests
タブに以下のようなスクリプトを記述します。
let ok = responseCode.code === 201
if(ok){
pm.globals.set('iLO_X-Auth-Token', responseHeaders['X-Auth-Token']);
pm.globals.set('iLO_Location', responseHeaders.Location);
}
また、グローバル変数にX-Auth-Token
とLocation
を用意しておきます。変数によってはベンダ間でフォーマットが微妙に違ったりすることがあったりするので、そういう差異を吸収できるよう私は2段構えにしています。
今回の場合は、X-Auth-Token
は大丈夫なのですが、Location
はベンダ間で違いがあります。HPE iLOのLocation
はIPアドレスを含めて返ってくるのですが(この記事で既出のセッション作成のスクリーンショットで確認できます)、Dell iDRACのLocation
はサービスルート以下で返ってきます。
なので、Location
はiLO_Location
を参照することにして、iLO_Location
という変数にレスポンスヘッダーで渡された値を設定しています。ちなみにiDRAC用にもidrac_Location
という変数を用意して、それを参照するLocation
という変数をもう一つ用意しておき、使う際にグローバル変数の編集画面でどちらを使うか切り替えています。(画面には使用中の変数しか表示されていません)
そして今度は設定した変数を利用してセッションを終了してみたいと思います。
そのためには、セッションのDELETE
リクエストのターゲットURIに変数Location
を指定し、ヘッダーにX-Auth-Token
を指定します。
この状態でリクエストを投げると無事にセッションが終了できました。
おまけ - コレクション変数や環境変数を使う場合のスクリプト
Postmanの変数には何種類かのスコープがあって、Redfishのレスポンスボディーで渡されたURIをクリックして新しいリクエストを投げるような使い方をする場合はいちいちコレクションに保存しないくていいのでグローバル変数が楽ですよと前述しました。
ただし、リクエストを保存していって他の人とコレクションをシェアするような場合はコレクション変数を使った方が変数がなくて動かないというエラーがなくて親切ですね。
コレクション変数を使う場合は、コレクション変数を用意して以下のスクリプトをTests
タブに記述します。
let ok = responseCode.code === 201
if(ok){
pm.collectionVariables.set('idrac_X-Auth-Token', responseHeaders['X-Auth-Token']);
pm.collectionVariables.set('idrac_Location', responseHeaders.Location);
}
ついでで環境変数を使いたい場合も同様に、環境変数を用意して以下のスクリプトをTests
タブに記述すると大丈夫です。
let ok = responseCode.code === 201
if(ok){
pm.environment.set('idrac_X-Auth-Token', responseHeaders['X-Auth-Token']);
pm.environment.set('idrac_Location', responseHeaders.Location);
}
まとめ
以上、簡単ですがPostmanでRedfishを触ってみました。キリがいいので本投稿はここまでにします。
次回の投稿でもう少し実践的なサーバーの操作やRedfishをもう少し詳しく紹介したいと思います。
Discussion