Rubyから「さくらのクラウド」を操ろう 2ページ
RubyでさくらのクラウドAPIを使用する
RubyではHTTPを扱うための標準ライブラリとして「net/http」が用意されているが、さくらのクラウドAPIではHTTPSを使った暗号化通信が必須となっているので、net/httpにSSL/TSL拡張を実装する「net/https」を利用することになる。
net/httpsライブラリでは、まずhttpオブジェクトを作成し、そこでSSL関連の設定を行った後、リクエストの種類に応じたリクエストオブジェクトを生成してリクエスト情報を格納し、startメソッドやrequestメソッドを使ってリクエストを送信する、という流れになる。
GETリクエストでAPIを実行する
以下のサンプルコードは、net/httpsを使ってサーバー一覧を取得する「/server」APIを利用するものだ。ここではまずNet::HTTP.newメソッドを使ってNet::HTTPオブジェクトを作成し、続いてNet::HTTP::Get.newメソッドでGETリクエストに対応したリクエストオブジェクトを生成、最後Net::HTTPクラスのstartメソッドおよびrequestメソッドを使ってHTTPセッションの開始やリクエストの送信を行っている。
実際に自分の環境でテストする場合、「<アクセストークン>」や「<アクセストークンシークレット>」の部分は適宜利用するさくらのクラウドアカウントに対応するものに置き換えて欲しい。また、ここではゾーンとして石狩第一(「is1a」)を指定しているが、石狩第二ゾーンを利用するする場合は代わりに「is1b」を指定する。
# -*- coding: utf-8 -*- require 'net/https' require 'uri' # アクセストークンおよびゾーン、APIバージョンを指定 token = '<アクセストークン>' secret = '<アクセストークンシークレット>' zone_id = 'is1a' api_version = '1.1' # アクセスするためのURLを生成 base_url = "https://secure.sakura.ad.jp/cloud/zone/#{zone_id}/api/cloud/#{api_version}" # 「/servers」にGETリクエストを送信する uri = URI.parse(base_url + '/server') # HTTPSを使うための設定 https = Net::HTTP.new(uri.host, uri.port) https.use_ssl = true https.verify_mode = OpenSSL::SSL::VERIFY_NONE # BASIC認証のための設定 req = Net::HTTP::Get.new(uri.path) req.basic_auth(token, secret) # リクエスト送信 res = https.start do |x| x.request(req) end # 取得したレスポンスを表示 puts res.body
このコードを実行すると、次のようにJSON形式で指定したゾーン上で稼動している仮想サーバー一覧が表示される。
{"From":0,"Count":11,"Total":11,"Servers":[{"Index":0,"ID":"************","Name":"centos 77","HostName":"localhost","Description":"","ServiceClass":"cloud\/plan\/1core-1gb","CreatedAt":"2014-04-14T18:50:27+09:00","Icon":{"ID":"112300511981","URL":"https:\/\/secure.sakura.ad.jp\/cloud\/zone\/is1a\/api\/cloud\/1.1\/icon\/112300511981.png","Name":"CentOS","Scope":"shared"},"ServerPlan":{"ID":1001,"Name":"\u30d7\u30e9\u30f3\/1Core-1GB","CPU":1,"MemoryMB":1024,"ServiceClass":"cloud\/plan\/1core-1gb","Availability":"available"},"Zone":{"ID":31001,"Name":"is1a","Description":"\u77f3\u72e9\u7b2c1\u30be\u30fc\u30f3","VNCProxy":{"HostName":"sac-is1a-ssl.sakura.ad.jp","IPAddress":"133.242.31.244"},"FTPServer":{"HostName":"sac-is1a-ssl.sakura.ad.jp","IPAddress":"133.242.31.244"},"Region":{"ID":310,"Name":"\u77f3\u72e9","Description":"\u77f3\u72e9","NameServers":["133.242.0.3","133.242.0.4"]}},"Instance":{"Server":{"ID":"************"},"Status":"up","BeforeStatus":"down", : :
さて、さくらのクラウドAPIではこの例のようにリソースの検索や一覧を行うAPIを実行した場合、その結果は次のような形式で返される。
{ Total: 検索条件にマッチする全レコード数: int, From: 取得された最初のレコードのオフセット: int, Count: 取得されたレコード数: int, リソース名: [ リソース情報: object, リソース情報: object, ... ] }
たとえば取得できたレコード数を知りたければ、返されたJSONデータの「Total」要素を参照すれば良いわけだ。これをプログラムで実現するには、返されたJSONデータをプログラム内で処理しやすいHash型に変換し、目的の要素を参照すれば良い。たとえば取得されたレコード数を調べるには、以下のような処理を行えば良い。
# 取得したJSON形式のレスポンスをオブジェクトに変換 result = JSON.parse(res.body) # Total要素の値を出力する puts "Total: " + result['Total'].to_s
また、人間が読みやすいように整形された形で取得したJSONデータを表示するには、次のようにしてHash型に変換した出力データをJSON.pretty_generateメソッドで整形して出力すれば良い。
puts JSON.pretty_generate(result)
なお、どのAPIがどのような情報をどういった構造で返すかはド各キュメントの「オブジェクト構造」に記載されている。
POSTリクエストでAPIを実行する
続いてはPOSTリクエストで実行するAPIの利用法について説明しよう。POSTリクエストの場合、GETリクエストとは異なりリクエストに適切なパラメータを与える必要がある。これには、リクエストに関する情報を格納するNet::HTTP:Postオブジェクトを作り、そこに送信するパラメータをJSON形式で格納すれば良い。具体的には以下のようなコードになる。
# BASIC認証のための設定 req = Net::HTTP::Post.new(uri.path) req.basic_auth(token, secret) # 送信するパラメータ params = { ハッシュ形式で送信するパラメータを指定 : : } # リクエストボディにJSON形式に変換したパラメータを格納 req.body=(JSON.generate(params))
たとえば、サーバーを作成するAPIは「POST /server」となっているが、この処理を実行するスクリプトは以下のようになる。
# -*- coding: utf-8 -*- require 'net/https' require 'uri' require 'json' # アクセストークンおよびゾーン、APIバージョンを指定 token = '<アクセストークン>' secret = '<アクセストークンシークレット>' zone_id = 'is1a' api_version = '1.1' # アクセスするためのURLを生成 base_url = "https://secure.sakura.ad.jp/cloud/zone/#{zone_id}/api/cloud/#{api_version}" # 「/servers」にPOSTリクエストを送信する uri = URI.parse(base_url + '/server') # HTTPSを使うための設定 https = Net::HTTP.new(uri.host, uri.port) https.use_ssl = true https.verify_mode = OpenSSL::SSL::VERIFY_NONE # BASIC認証のための設定 req = Net::HTTP::Post.new(uri.path) req.basic_auth(token, secret) # 送信するパラメータ params = { 'Server' => { 'Name' => 'foobar', # サーバー名 'Zone' => { 'ID' => 31001 }, # ゾーンID 'ServerPlan' => { 'ID' => 1001 }, # サーバープランID } } req.body=(JSON.generate(params)) # リクエスト送信 res = https.start do |x| x.request(req) end # 201以外のHTTPステータスコードが返ってきたら終了する unless res.code == '201' puts res.code + ': ' + res.message exit end # 取得したJSON形式のレスポンスをオブジェクトに変換 result = JSON.parse(res.body) # 作成したサーバーのサーバーIDなどを出力 printf("Server %s created. ID: %d\n", result["Server"]["Name"], result["Server"]["ID"]) puts result
送信するパラメータについて詳しくはAPIドキュメントを参照してほしいが、ここではサーバー名およびゾーンID、サーバープランIDを送信している。
なお、ゾーンIDやサーバープランIDはドキュメントには記載されていないので、APIを使って取得する必要がある。ゾーンIDを取得するAPIについてはAPIドキュメントの設備関連APIページに、サーバープランIDについては商品関連APIに記載があるが、たとえばゾーンIDを取得するAPI(GET /zone)を実行した場合、次のような結果が得られる。ここから、石狩第1ゾーンのIDは「31001」、第2ゾーンのIDは「31002」であることが分かる。
{ "From": 0, "Count": 2, "Total": 2, "Zones": [ { "Index": 0, "ID": 31001, "Name": "is1a", "Description": "石狩第1ゾーン", "VNCProxy": { "HostName": "sac-is1a-ssl.sakura.ad.jp", "IPAddress": "133.242.31.244" }, "FTPServer": { "HostName": "sac-is1a-ssl.sakura.ad.jp", "IPAddress": "133.242.31.244" }, "CreatedAt": "2011-11-03T22:41:11+09:00", "Region": { "ID": 310, "Name": "石狩", "Description": "石狩", "NameServers": [ "133.242.0.3", "133.242.0.4" ] } }, { "Index": 1, "ID": 31002, "Name": "is1b", "Description": "石狩第2ゾーン", "VNCProxy": { "HostName": "sac-is1b-ssl.sakura.ad.jp", "IPAddress": "133.242.239.244" }, "FTPServer": { "HostName": "sac-is1b-ssl.sakura.ad.jp", "IPAddress": "133.242.239.244" }, "CreatedAt": "2013-10-01T07:01:04+09:00", "Region": { "ID": 310, "Name": "石狩", "Description": "石狩", "NameServers": [ "133.242.0.3", "133.242.0.4" ] } } ], "is_ok": true }
また、サーバーIDを取得するAPI(GET /product/server)を実行した場合、次のような結果が得られる。出力が長くなるので割愛しているが、プランIDは「<メモリ容量><コア数(3桁)>」という数字となり、たとえば1コア、メモリ1GBの場合「1001」、2コア、メモリ1GBの場合は「2001」、12コア、メモリ128GBの場合は「128012」となっている。
{ "From": 0, "Count": 42, "Total": 42, "ServerPlans": [ { "Index": 0, "ID": 1001, "Name": "プラン/1Core-1GB", "Description": "", "CPU": 1, "MemoryMB": 1024, "ServiceClass": "cloud/plan/1core-1gb", "Availability": "available" }, { "Index": 1, "ID": 2001, "Name": "プラン/1Core-2GB", "Description": "", "CPU": 1, "MemoryMB": 2048, "ServiceClass": "cloud/plan/1core-2gb", "Availability": "available" }, { "Index": 2, "ID": 2002, "Name": "プラン/2Core-2GB", "Description": "", "CPU": 2, "MemoryMB": 2048, "ServiceClass": "cloud/plan/2core-2gb", "Availability": "available" }, : :」 { "Index": 40, "ID": 96012, "Name": "プラン/12Core-96GB", "Description": "", "CPU": 12, "MemoryMB": 98304, "ServiceClass": "cloud/plan/12core-96gb", "Availability": "available" }, { "Index": 41, "ID": 128012, "Name": "プラン/12Core-128GB", "Description": "", "CPU": 12, "MemoryMB": 131072, "ServiceClass": "cloud/plan/12core-128gb", "Availability": "available" } ], "is_ok": true
PUT/DELETEリクエストでAPIを実行する
さくらのクラウドAPIではGETおよびPOST以外に、PUTやDELETEといったリクエストも利用される。PUTはリソースの設定を行う際に、DELETEはリソースの開放を行う際に使用するリクエストだ。Rubyのnet/httpライブラリでは、これらは「Net::HTTP::Put」および「Net::HTTP::Delete」クラスで実装されており、それぞれのインスタンスを作成してrequestメソッドの引数として与えることでリクエストを送信できる。なお、さくらのクラウドAPIでは、PUTリクエストを行う際はリクエストボディが空の場合でもContent-Lengthヘッダが必須となっているようだ。この場合、以下のように空のデータをリクエストボディに設定してやれば良い。
# BASIC認証のための設定 req = Net::HTTP::Put.new(uri.path) req.basic_auth(token, secret) # 送信するパラメータの設定 req.body=(JSON.generate({}))