こんにちは、たびとです。
前回、Redfish のモックアップから Redfish のファイルを読み込むコードを作りましたが、 今回はファイル読み込みの箇所を Web 接続して、サーバ(BMC)から情報を取得できるように修正してみました。
前回は、Redfish に興味があってもサーバ(BMC)がないとどうしようもないため、 モックアップの JSON をファイル化して、疑似的に試してみました。 前回の記事はこちらから。
この記事の対象者
- サーバ(BMC)から Redfish の情報を一気に取得したいと思っている方
- Redfish などの REST API の作り方に興味のある方
ファイルから Web へ
前回のソースのファイルアクセス箇所を HTTP アクセスを簡単に実装するため、RestSharp を使うことにします。
気を付けるポイントは、サーバ(BMC)の Redfish が SSL 用の自己証明書を使っている場合、 RemoteCertificateValidationCallback を使って、ture を返す必要があります。
また、実際にサーバ(BMC)から Redfish 情報を取得したところ、10 数 MB から 100Mb のサイズになったので、 コンソールへの出力からファイルの出力に変更しました。
RestSharp を使ったソースコード
RestSharp の実装は、以下の手順を組み込むだけです。
- 最初に一度 RestClient を作成する。SSL 用の自己証明書に備えて、RestClientOptions で RemoteCertificateValidationCallback を定義する。
- Redfish の URL を渡されたら RestRequest でリクエストを作成する。必要に応じて、AddHeader でヘッダ情報を指定する。
- RestSharp の ExecuteAsync or Execute メソッドを呼び出して、RestResponse を取得する。
using Newtonsoft.Json; using RestSharp; using RestSharp.Authenticators; var hostname = "サーバ(BMC)のIPアドレス"; var userName = "ユーザ名"; var password = "パスワード"; var filename = $@"C:\tmp\redfish_{hostname}.json"; var options = new RestClientOptions($"https://{hostname}") { RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true }; var redfish = new Redfish { Client = new RestClient(options) { Authenticator = new HttpBasicAuthenticator(userName, password) } }; redfish.Read($"/redfish/v1/"); redfish.Write(filename); Console.WriteLine("finished."); /// <summary> /// JSONの要素 /// </summary> class JsonElement { public JsonToken TokenType { get; set; } public object? Value { get; set; } } /// <summary> /// Redfishマージ用 /// </summary> class Redfish { public RestClient? Client { get; set; } public List<JsonElement> Elements { get; set; } = new List<JsonElement>(); HashSet<JsonToken> JsonTags { get; } = new HashSet<JsonToken>() { JsonToken.StartObject, JsonToken.EndObject, JsonToken.StartArray, JsonToken.EndArray }; HashSet<string> OdataItems { get; set; } = new HashSet<string>() { "/redfish/v1/" }; /// <summary> /// 全てを読み込む /// </summary> /// <param name="path"></param> public void Read(string path) { if (Client == null) return; // Redfish 情報取得 var request = new RestRequest(path); request.AddHeader("Content-Type", "application/json"); var response = Client.ExecuteAsync(request, Method.Get).Result; if (response == null || response.Content == null || response.ResponseUri == null) return; Console.WriteLine($"{response.ResponseUri.AbsoluteUri}\n{response.Content}\n"); // Redfish情報の読み込み var reader = new JsonTextReader(new StringReader(response.Content)); while (reader.Read()) { var val1 = reader.Value; Elements.Add(new JsonElement() { TokenType = reader.TokenType, Value = val1 }); if (reader.TokenType != JsonToken.PropertyName) continue; // プロパティ以外 reader.Read(); // プロパティの値を読み込む var val2 = reader.Value; Elements.Add(new JsonElement() { TokenType = reader.TokenType, Value = val2 }); if (val1 == null || val2 == null) continue; // 警告対策 if (JsonTags.Contains(reader.TokenType)) continue; // {} or [] 出現 if (!((string)val1).Equals("@odata.id")) continue; // @odata.id 以外 if (OdataItems.Contains((string)val2)) continue; // 同じ @odata.id が出現 // @odata.id 処理 OdataItems.Add((string)val2); // "@odata.id" 値を保持 Elements.Add(new JsonElement() { TokenType = JsonToken.PropertyName, Value = "/* @odata.child */" }); Read((string)val2); // 再帰 } } /// <summary> /// マージしたJSONを出力する /// </summary> public void Write(string filename) { using var sw = new StreamWriter(filename); using var writer = new JsonTextWriter(sw); writer.Formatting = Formatting.Indented; foreach (var elem in Elements) { switch (elem.TokenType) { case JsonToken.StartObject: writer.WriteStartObject(); break; case JsonToken.EndObject: writer.WriteEndObject(); break; case JsonToken.StartArray: writer.WriteStartArray(); break; case JsonToken.EndArray: writer.WriteEndArray(); break; case JsonToken.PropertyName: writer.WritePropertyName((string)(elem.Value ?? "")); break; default: writer.WriteValue(elem.Value); break; } } } }
RestSharp の注意点
以前は、GetAsync() メソッドで記載していましたが、使い勝手が悪いため、 ExecuteAsync() に変更しました。
今回は GET リクエストのみなので、GetAsync() を採用してみましたが、 このメソッドは、認証エラーでも例外が発生します。 認証エラーの場合、HTTP ステータスコード 401 (Unauthrorized) を返して欲しいところです。
簡易的なプログラムであれば、それでもいいのでしょうが、 HTTP ステータスコードを使って判定したい場合、 ExecuteAsync(request, Method.Get) に変更する必要があります。 エラーの場合、response.ErrorException に例外が格納されるため、 HTTP レスポンスが返ってきた後に制御することが可能となります。
まとめ
このソースコードを使って、何台かのサーバ(BMC)で試してみました。 100 MB の JSON を取得したときは、例外で止まったので、 エラー処理やリトライ処理を実装して取得しました。
ただし、ここに掲載しているソースコードは、前回との対比もあり、 できるだけ簡単に Redfish にアクセスする方法を紹介しているため、 リトライ&エラー処理は省略しています。
今後、機会があれば、データベースを活用し、 もっと使いやすい GUI 版の Redfish ツールを作ってみたいと思います。
では、皆さん、よい旅を。